• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

FIWARE / VCVerifier / 18713115930

22 Oct 2025 10:23AM UTC coverage: 43.214% (-1.0%) from 44.17%
18713115930

push

github

web-flow
Merge pull request #67 from FIWARE/token-exchang

Token exchange

171 of 426 new or added lines in 6 files covered. (40.14%)

3 existing lines in 3 files now uncovered.

1592 of 3684 relevant lines covered (43.21%)

0.49 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

11.48
/verifier/presentation_parser.go
1
package verifier
2

3
import (
4
        "context"
5
        "encoding/base64"
6
        "encoding/json"
7
        "errors"
8
        "net/http"
9
        "strings"
10
        "time"
11

12
        configModel "github.com/fiware/VCVerifier/config"
13
        "github.com/fiware/VCVerifier/jades"
14
        "github.com/fiware/VCVerifier/logging"
15
        "github.com/hellofresh/health-go/v5"
16
        "github.com/piprate/json-gold/ld"
17
        "github.com/trustbloc/vc-go/jwt"
18
        "github.com/trustbloc/vc-go/proof/defaults"
19
        sdv "github.com/trustbloc/vc-go/sdjwt/verifier"
20
        "github.com/trustbloc/vc-go/verifiable"
21
)
22

23
var ErrorNoValidationEndpoint = errors.New("no_validation_endpoint_configured")
24
var ErrorNoValidationHost = errors.New("no_validation_host_configured")
25
var ErrorInvalidSdJwt = errors.New("credential_is_not_sd_jwt")
26
var ErrorPresentationNoCredentials = errors.New("presentation_not_contains_credentials")
27
var ErrorInvalidProof = errors.New("invalid_vp_proof")
28

29
var proofChecker = defaults.NewDefaultProofChecker(JWTVerfificationMethodResolver{})
30
var defaultPresentationOptions = []verifiable.PresentationOpt{
31
        verifiable.WithPresProofChecker(proofChecker),
32
        verifiable.WithPresJSONLDDocumentLoader(NewCachingDocumentLoader(ld.NewDefaultDocumentLoader(http.DefaultClient)))}
33

34
var defaultSdJwtParserOptions = []sdv.ParseOpt{
35
        sdv.WithSignatureVerifier(proofChecker),
36
        sdv.WithHolderVerificationRequired(false),
37
        sdv.WithHolderSigningAlgorithms([]string{"ES256", "PS256"}),
38
        sdv.WithIssuerSigningAlgorithms([]string{"ES256", "PS256"}),
39
}
40

41
// allow singleton access to the parser
42
var presentationParser PresentationParser
43

44
// allow singleton access to the parser
45
var sdJwtParser SdJwtParser
46

47
// parser interface
48
type PresentationParser interface {
49
        ParsePresentation(tokenBytes []byte) (*verifiable.Presentation, error)
50
}
51

52
type SdJwtParser interface {
53
        Parse(tokenString string) (map[string]interface{}, error)
54
        ParseWithSdJwt(tokenBytes []byte) (presentation *verifiable.Presentation, err error)
55
}
56

57
type ConfigurablePresentationParser struct {
58
        PresentationOpts []verifiable.PresentationOpt
59
}
60

61
type ConfigurableSdJwtParser struct {
62
        ParserOpts []sdv.ParseOpt
63
}
64

65
/**
66
* Global singelton access to the parser
67
**/
68
func GetSdJwtParser() SdJwtParser {
×
69
        if sdJwtParser == nil {
×
70
                logging.Log().Error("SdJwtParser is not initialized.")
×
71
        }
×
72
        return sdJwtParser
×
73
}
74

75
/**
76
* Global singelton access to the parser
77
**/
78
func GetPresentationParser() PresentationParser {
×
79
        if presentationParser == nil {
×
80
                logging.Log().Error("PresentationParser is not initialized.")
×
81
        }
×
82
        return presentationParser
×
83
}
84

85
// init the presentation parser depending on the config, either with or without did:elsi support
86
func InitPresentationParser(config *configModel.Configuration, healthCheck *health.Health) error {
×
87
        elsiConfig := &config.Elsi
×
88
        err := validateConfig(elsiConfig)
×
89
        if err != nil {
×
90
                logging.Log().Warnf("No valid elsi configuration provided. Error: %v", err)
×
91
                return err
×
92
        }
×
93
        if elsiConfig.Enabled {
×
94
                jAdESValidator := &jades.ExternalJAdESValidator{
×
95
                        HttpClient:        &http.Client{},
×
96
                        ValidationAddress: buildAddress(elsiConfig.ValidationEndpoint.Host, elsiConfig.ValidationEndpoint.ValidationPath),
×
97
                        HealthAddress:     buildAddress(elsiConfig.ValidationEndpoint.Host, elsiConfig.ValidationEndpoint.HealthPath)}
×
98

×
NEW
99
                proofChecker := &ElsiProofChecker{
×
100
                        defaultChecker: defaults.NewDefaultProofChecker(JWTVerfificationMethodResolver{}),
×
101
                        jAdESValidator: jAdESValidator,
×
102
                }
×
103

×
104
                healthCheck.Register(health.Config{
×
105
                        Name:      "JAdES-Validator",
×
106
                        Timeout:   time.Second * 5,
×
107
                        SkipOnErr: false,
×
108
                        Check: func(ctx context.Context) error {
×
109
                                return jAdESValidator.IsReady()
×
110
                        },
×
111
                })
112

113
                presentationParser = &ConfigurablePresentationParser{PresentationOpts: []verifiable.PresentationOpt{
×
NEW
114
                        verifiable.WithPresProofChecker(proofChecker),
×
115
                        verifiable.WithPresJSONLDDocumentLoader(NewCachingDocumentLoader(ld.NewDefaultDocumentLoader(http.DefaultClient)))}}
×
116
        } else {
×
117
                presentationParser = &ConfigurablePresentationParser{PresentationOpts: defaultPresentationOptions}
×
118
        }
×
119
        sdJwtParser = &ConfigurableSdJwtParser{ParserOpts: defaultSdJwtParserOptions}
×
120

×
121
        return nil
×
122
}
123

124
func validateConfig(elsiConfig *configModel.Elsi) error {
1✔
125
        if !elsiConfig.Enabled {
2✔
126
                return nil
1✔
127
        }
1✔
128
        if elsiConfig.ValidationEndpoint == nil {
2✔
129
                return ErrorNoValidationEndpoint
1✔
130
        }
1✔
131
        if elsiConfig.ValidationEndpoint.Host == "" {
2✔
132
                return ErrorNoValidationHost
1✔
133
        }
1✔
134
        return nil
1✔
135
}
136

137
func buildAddress(host, path string) string {
1✔
138
        return strings.TrimSuffix(host, "/") + "/" + strings.TrimPrefix(path, "/")
1✔
139
}
1✔
140

141
func (cpp *ConfigurablePresentationParser) ParsePresentation(tokenBytes []byte) (*verifiable.Presentation, error) {
×
142
        return verifiable.ParsePresentation(tokenBytes, cpp.PresentationOpts...)
×
143
}
×
144

145
func (sjp *ConfigurableSdJwtParser) Parse(tokenString string) (map[string]interface{}, error) {
×
146
        return sdv.Parse(tokenString, sjp.ParserOpts...)
×
147
}
×
148

NEW
149
func (sjp *ConfigurableSdJwtParser) ClaimsToCredential(claims map[string]interface{}) (credential *verifiable.Credential, err error) {
×
NEW
150

×
NEW
151
        issuer, i_ok := claims["iss"]
×
NEW
152
        vct, vct_ok := claims["vct"]
×
NEW
153
        if !i_ok || !vct_ok {
×
NEW
154
                logging.Log().Infof("Token does not contain issuer(%v) or vct(%v).", i_ok, vct_ok)
×
NEW
155
                return credential, ErrorInvalidSdJwt
×
NEW
156
        }
×
NEW
157
        customFields := verifiable.CustomFields{}
×
NEW
158
        for k, v := range claims {
×
NEW
159
                if k != "iss" && k != "vct" {
×
NEW
160
                        customFields[k] = v
×
NEW
161
                }
×
162
        }
NEW
163
        subject := verifiable.Subject{CustomFields: customFields}
×
NEW
164
        contents := verifiable.CredentialContents{Issuer: &verifiable.Issuer{ID: issuer.(string)}, Types: []string{vct.(string)}, Subject: []verifiable.Subject{subject}}
×
NEW
165
        return verifiable.CreateCredential(contents, verifiable.CustomFields{})
×
166
}
167

NEW
168
func (sjp *ConfigurableSdJwtParser) ParseWithSdJwt(tokenBytes []byte) (presentation *verifiable.Presentation, err error) {
×
NEW
169
        logging.Log().Debug("Parse with SD-Jwt")
×
NEW
170

×
NEW
171
        tokenString := string(tokenBytes)
×
NEW
172
        payloadString := strings.Split(tokenString, ".")[1]
×
NEW
173
        payloadBytes, _ := base64.RawURLEncoding.DecodeString(payloadString)
×
NEW
174

×
NEW
175
        var vpMap map[string]interface{}
×
NEW
176
        if err := json.Unmarshal(payloadBytes, &vpMap); err != nil {
×
NEW
177
                return nil, err
×
NEW
178
        }
×
179

NEW
180
        vp, ok := vpMap["vp"].(map[string]interface{})
×
NEW
181
        if !ok {
×
NEW
182
                return presentation, ErrorPresentationNoCredentials
×
NEW
183
        }
×
184

NEW
185
        vcs, ok := vp["verifiableCredential"]
×
NEW
186
        if !ok {
×
NEW
187
                return presentation, ErrorPresentationNoCredentials
×
NEW
188
        }
×
189

NEW
190
        presentation, err = verifiable.NewPresentation()
×
NEW
191
        if err != nil {
×
NEW
192
                return nil, err
×
NEW
193
        }
×
194

NEW
195
        presentation.Holder = vp["holder"].(string)
×
NEW
196

×
NEW
197
        // due to dcql, we only need to take care of presentations containing credentials of the same type.
×
NEW
198
        for _, vc := range vcs.([]interface{}) {
×
NEW
199
                logging.Log().Debugf("The vc %s", vc.(string))
×
NEW
200
                parsed, err := sjp.Parse(vc.(string))
×
NEW
201
                if err != nil {
×
NEW
202
                        return nil, err
×
NEW
203
                }
×
NEW
204
                credential, err := sjp.ClaimsToCredential(parsed)
×
NEW
205
                if err != nil {
×
NEW
206
                        return nil, err
×
NEW
207
                }
×
NEW
208
                presentation.AddCredentials(credential)
×
209
        }
210

NEW
211
        err = jwt.CheckProof(string(tokenBytes), proofChecker, nil, nil)
×
NEW
212
        if err != nil {
×
NEW
213
                return nil, ErrorInvalidProof
×
NEW
214
        }
×
215

NEW
216
        return presentation, nil
×
217
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc