Skip to content

Commit

Permalink
Discovery: require aud claim to be service ID
Browse files Browse the repository at this point in the history
  • Loading branch information
reinkrul committed Dec 8, 2023
1 parent 1533c5a commit cbbdea0
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 5 deletions.
14 changes: 14 additions & 0 deletions discovery/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ func (m *Module) Add(serviceID string, presentation vc.VerifiablePresentation) e
if presentation.ID == nil {
return errors.New("presentation does not have an ID")
}
// Make sure the presentation is intended for this service
if err := validateAudience(definition, presentation.JWT().Audience()); err != nil {
return err
}
expiration := presentation.JWT().Expiration()
if expiration.IsZero() {
return errors.New("presentation does not have an expiration")
Expand Down Expand Up @@ -206,6 +210,16 @@ func (m *Module) validateRetraction(serviceID string, presentation vc.Verifiable
return nil
}

// validateAudience checks if the given audience of the presentation matches the service ID.
func validateAudience(service ServiceDefinition, audience []string) error {
for _, audienceID := range audience {
if audienceID == service.ID {
return nil
}
}
return errors.New("aud claim is missing or invalid")
}

func (m *Module) Get(serviceID string, startAt Timestamp) ([]vc.VerifiablePresentation, *Timestamp, error) {
if _, exists := m.services[serviceID]; !exists {
return nil, nil, ErrServiceNotFound
Expand Down
18 changes: 15 additions & 3 deletions discovery/module_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package discovery

import (
"errors"
"github.com/lestrrat-go/jwx/v2/jwt"
"github.com/nuts-foundation/go-did/vc"
"github.com/nuts-foundation/nuts-node/core"
"github.com/nuts-foundation/nuts-node/storage"
Expand Down Expand Up @@ -82,6 +83,7 @@ func Test_Module_Add(t *testing.T) {
t.Run("no expiration", func(t *testing.T) {
m, _ := setupModule(t, storageEngine)
err := m.Add(testServiceID, createPresentationCustom(aliceDID, func(claims map[string]interface{}, _ *vc.VerifiablePresentation) {
claims[jwt.AudienceKey] = []string{testServiceID}
delete(claims, "exp")
}))
assert.EqualError(t, err, "presentation does not have an expiration")
Expand All @@ -90,6 +92,7 @@ func Test_Module_Add(t *testing.T) {
m, _ := setupModule(t, storageEngine)

vpWithoutID := createPresentationCustom(aliceDID, func(claims map[string]interface{}, _ *vc.VerifiablePresentation) {
claims[jwt.AudienceKey] = []string{testServiceID}
delete(claims, "jti")
}, vcAlice)
err := m.Add(testServiceID, vpWithoutID)
Expand Down Expand Up @@ -122,17 +125,22 @@ func Test_Module_Add(t *testing.T) {
m, _ := setupModule(t, storageEngine)

vcAlice := createCredential(authorityDID, aliceDID, nil, func(claims map[string]interface{}) {
claims[jwt.AudienceKey] = []string{testServiceID}
claims["exp"] = time.Now().Add(time.Hour)
})
vpAlice := createPresentation(aliceDID, vcAlice)
vpAlice := createPresentationCustom(aliceDID, func(claims map[string]interface{}, vp *vc.VerifiablePresentation) {
claims[jwt.AudienceKey] = []string{testServiceID}
}, vcAlice)
err := m.Add(testServiceID, vpAlice)
assert.EqualError(t, err, "presentation is valid longer than the credential(s) it contains")
})
t.Run("not conform to Presentation Definition", func(t *testing.T) {
m, _ := setupModule(t, storageEngine)

// Presentation Definition only allows did:example DIDs
otherVP := createPresentation(unsupportedDID, createCredential(unsupportedDID, unsupportedDID, nil, nil))
otherVP := createPresentationCustom(unsupportedDID, func(claims map[string]interface{}, vp *vc.VerifiablePresentation) {
claims[jwt.AudienceKey] = []string{testServiceID}
}, createCredential(unsupportedDID, unsupportedDID, nil, nil))
err := m.Add(testServiceID, otherVP)
require.ErrorContains(t, err, "presentation does not fulfill Presentation ServiceDefinition")

Expand All @@ -144,6 +152,7 @@ func Test_Module_Add(t *testing.T) {
vpAliceRetract := createPresentationCustom(aliceDID, func(claims map[string]interface{}, vp *vc.VerifiablePresentation) {
vp.Type = append(vp.Type, retractionPresentationType)
claims["retract_jti"] = vpAlice.ID.String()
claims[jwt.AudienceKey] = []string{testServiceID}
})
t.Run("ok", func(t *testing.T) {
m, presentationVerifier := setupModule(t, storageEngine)
Expand All @@ -163,14 +172,16 @@ func Test_Module_Add(t *testing.T) {
m, _ := setupModule(t, storageEngine)
vp := createPresentationCustom(aliceDID, func(claims map[string]interface{}, vp *vc.VerifiablePresentation) {
vp.Type = append(vp.Type, retractionPresentationType)
claims[jwt.AudienceKey] = []string{testServiceID}
}, vcAlice)
err := m.Add(testServiceID, vp)
assert.EqualError(t, err, "retraction presentation must not contain credentials")
})
t.Run("missing 'retract_jti' claim", func(t *testing.T) {
m, _ := setupModule(t, storageEngine)
vp := createPresentationCustom(aliceDID, func(_ map[string]interface{}, vp *vc.VerifiablePresentation) {
vp := createPresentationCustom(aliceDID, func(claims map[string]interface{}, vp *vc.VerifiablePresentation) {
vp.Type = append(vp.Type, retractionPresentationType)
claims[jwt.AudienceKey] = []string{testServiceID}
})
err := m.Add(testServiceID, vp)
assert.EqualError(t, err, "retraction presentation does not contain 'retract_jti' claim")
Expand All @@ -180,6 +191,7 @@ func Test_Module_Add(t *testing.T) {
vp := createPresentationCustom(aliceDID, func(claims map[string]interface{}, vp *vc.VerifiablePresentation) {
vp.Type = append(vp.Type, retractionPresentationType)
claims["retract_jti"] = 10
claims[jwt.AudienceKey] = []string{testServiceID}
})
err := m.Add(testServiceID, vp)
assert.EqualError(t, err, "retraction presentation 'retract_jti' claim is not a string")
Expand Down
8 changes: 6 additions & 2 deletions discovery/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,14 +115,18 @@ func init() {
"familyName": "Jones",
},
}, nil)
vpAlice = createPresentation(aliceDID, vcAlice)
vpAlice = createPresentationCustom(aliceDID, func(claims map[string]interface{}, vp *vc.VerifiablePresentation) {
claims[jwt.AudienceKey] = []string{testServiceID}
}, vcAlice)
vcBob = createCredential(authorityDID, bobDID, map[string]interface{}{
"person": map[string]interface{}{
"givenName": "Bob",
"familyName": "Jomper",
},
}, nil)
vpBob = createPresentation(bobDID, vcBob)
vpBob = createPresentationCustom(bobDID, func(claims map[string]interface{}, vp *vc.VerifiablePresentation) {
claims[jwt.AudienceKey] = []string{testServiceID}
}, vcBob)
}

func createCredential(issuerDID did.DID, subjectDID did.DID, credentialSubject map[string]interface{}, claimVisitor func(map[string]interface{})) vc.VerifiableCredential {
Expand Down

0 comments on commit cbbdea0

Please sign in to comment.