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

FIWARE / VCVerifier / 17800152848

17 Sep 2025 02:01PM UTC coverage: 44.17% (-0.1%) from 44.317%
17800152848

push

github

web-flow
Merge pull request #66 from FIWARE/dcql

Support DCQL

24 of 60 new or added lines in 4 files covered. (40.0%)

1 existing line in 1 file now uncovered.

1485 of 3362 relevant lines covered (44.17%)

0.5 hits per line

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

58.67
/openapi/api_api.go
1
/*
2
 * vcverifier
3
 *
4
 * Backend component to verify credentials
5
 *
6
 * API version: 0.0.1
7
 * Generated by: OpenAPI Generator (https://openapi-generator.tech)
8
 */
9

10
package openapi
11

12
import (
13
        "encoding/base64"
14
        "encoding/json"
15
        "errors"
16
        "fmt"
17
        "net/http"
18
        "slices"
19
        "strings"
20

21
        "github.com/fiware/VCVerifier/common"
22
        "github.com/fiware/VCVerifier/logging"
23
        "github.com/fiware/VCVerifier/verifier"
24
        "github.com/lestrrat-go/jwx/v3/jwa"
25
        "github.com/lestrrat-go/jwx/v3/jwt"
26
        vdr_jwk "github.com/trustbloc/did-go/method/jwk"
27
        vdr_key "github.com/trustbloc/did-go/method/key"
28
        vdr_web "github.com/trustbloc/did-go/method/web"
29
        "github.com/trustbloc/did-go/vdr/api"
30
        "github.com/trustbloc/vc-go/verifiable"
31

32
        "github.com/gin-gonic/gin"
33
)
34

35
var apiVerifier verifier.Verifier
36
var presentationParser verifier.PresentationParser
37
var sdJwtParser verifier.SdJwtParser
38
var keyResolver verifier.KeyResolver
39

40
var ErrorMessagNoGrantType = ErrorMessage{"no_grant_type_provided", "Token requests require a grant_type."}
41
var ErrorMessageUnsupportedGrantType = ErrorMessage{"unsupported_grant_type", "Provided grant_type is not supported by the implementation."}
42
var ErrorMessageNoCode = ErrorMessage{"no_code_provided", "Token requests require a code."}
43
var ErrorMessageNoRedircetUri = ErrorMessage{"no_redirect_uri_provided", "Token requests require a redirect_uri."}
44
var ErrorMessageNoState = ErrorMessage{"no_state_provided", "Authentication requires a state provided as query parameter."}
45
var ErrorMessageNoScope = ErrorMessage{"no_scope_provided", "Authentication requires a scope provided as a parameter."}
46
var ErrorMessageNoNonce = ErrorMessage{"no_nonce_provided", "Authentication requires a nonce provided as a query parameter."}
47
var ErrorMessageNoToken = ErrorMessage{"no_token_provided", "Authentication requires a token provided as a form parameter."}
48
var ErrorMessageNoPresentationSubmission = ErrorMessage{"no_presentation_submission_provided", "Authentication requires a presentation submission provided as a form parameter."}
49
var ErrorMessageNoCallback = ErrorMessage{"NoCallbackProvided", "A callback address has to be provided as query-parameter."}
50
var ErrorMessageUnableToDecodeToken = ErrorMessage{"invalid_token", "Token could not be decoded."}
51
var ErrorMessageUnableToDecodeCredential = ErrorMessage{"invalid_token", "Could not read the credential(s) inside the token."}
52
var ErrorMessageUnableToDecodeHolder = ErrorMessage{"invalid_token", "Could not read the holder inside the token."}
53
var ErrorMessageNoSuchSession = ErrorMessage{"no_session", "Session with the requested id is not available."}
54
var ErrorMessageInvalidSdJwt = ErrorMessage{"invalid_sdjwt", "SdJwt does not contain all required fields."}
55
var ErrorMessageNoWebsocketConnection = ErrorMessage{"invalid_connection", "No Websocket connection available for the authenticated session."}
56
var ErrorMessageUnresolvableRequestObject = ErrorMessage{"unresolvable_request_object", "Was not able to get the request object from the client."}
57
var ErrorMessageInvalidAudience = ErrorMessage{"invalid_audience", "Audience of the request object was invalid."}
58
var ErrorMessageUnsupportedAssertionType = ErrorMessage{"unsupported_assertion_type", "Assertion type is not supported."}
59
var ErrorMessageInvalidClientAssertion = ErrorMessage{"invalid_client_assertion", "Provided client assertion is invalid."}
60
var ErrorMessageInvalidTokenRequest = ErrorMessage{"invalid_token_request", "Token request has no redirect_uri and no valid client assertion."}
61

62
func getApiVerifier() verifier.Verifier {
1✔
63
        if apiVerifier == nil {
1✔
64
                apiVerifier = verifier.GetVerifier()
×
65
        }
×
66
        return apiVerifier
1✔
67
}
68

69
func getPresentationParser() verifier.PresentationParser {
1✔
70
        if presentationParser == nil {
1✔
71
                presentationParser = verifier.GetPresentationParser()
×
72
        }
×
73
        return presentationParser
1✔
74
}
75

76
func getSdJwtParser() verifier.SdJwtParser {
1✔
77
        if sdJwtParser == nil {
1✔
78
                sdJwtParser = verifier.GetSdJwtParser()
×
79
        }
×
80
        return sdJwtParser
1✔
81
}
82

83
func getKeyResolver() verifier.KeyResolver {
×
84
        if keyResolver == nil {
×
85
                keyResolver = &verifier.VdrKeyResolver{Vdr: []api.VDR{vdr_key.New(), vdr_jwk.New(), vdr_web.New()}}
×
86
        }
×
87
        return keyResolver
×
88
}
89

90
// GetToken - Token endpoint to exchange the authorization code with the actual JWT.
91
func GetToken(c *gin.Context) {
1✔
92

1✔
93
        logging.Log().Debugf("%v", c.Request)
1✔
94
        grantType, grantTypeExists := c.GetPostForm("grant_type")
1✔
95
        if !grantTypeExists {
2✔
96
                logging.Log().Debug("No grant_type present in the request.")
1✔
97
                c.AbortWithStatusJSON(400, ErrorMessagNoGrantType)
1✔
98
                return
1✔
99
        }
1✔
100

101
        if grantType == common.TYPE_CODE {
2✔
102
                handleTokenTypeCode(c)
1✔
103
        } else if grantType == common.TYPE_VP_TOKEN {
3✔
104
                handleTokenTypeVPToken(c, c.GetHeader("client_id"))
1✔
105
        } else {
2✔
106
                c.AbortWithStatusJSON(400, ErrorMessageUnsupportedGrantType)
1✔
107
        }
1✔
108
}
109

110
// GetToken - Token endpoint to exchange the authorization code with the actual JWT.
111
func GetTokenForService(c *gin.Context) {
×
112

×
113
        logging.Log().Debugf("%v", c.Request)
×
114
        grantType, grantTypeExists := c.GetPostForm("grant_type")
×
115
        if !grantTypeExists {
×
116
                logging.Log().Debug("No grant_type present in the request.")
×
117
                c.AbortWithStatusJSON(400, ErrorMessagNoGrantType)
×
118
                return
×
119
        }
×
120

121
        if grantType == common.TYPE_CODE {
×
122
                handleTokenTypeCode(c)
×
123
        } else if grantType == common.TYPE_VP_TOKEN {
×
124
                handleTokenTypeVPToken(c, c.Param("service_id"))
×
125
        } else {
×
126
                c.AbortWithStatusJSON(400, ErrorMessageUnsupportedGrantType)
×
127
        }
×
128
}
129

130
func handleTokenTypeVPToken(c *gin.Context, clientId string) {
1✔
131
        var requestBody TokenRequestBody
1✔
132

1✔
133
        vpToken, vpTokenExists := c.GetPostForm("vp_token")
1✔
134
        if !vpTokenExists {
2✔
135
                logging.Log().Debug("No vp token present in the request.")
1✔
136
                c.AbortWithStatusJSON(400, ErrorMessageNoToken)
1✔
137
                return
1✔
138
        }
1✔
139

140
        logging.Log().Warnf("Got token %s", vpToken)
1✔
141

1✔
142
        // not used at the moment
1✔
143
        // presentationSubmission, presentationSubmissionExists := c.GetPostForm("presentation_submission")
1✔
144
        // if !presentationSubmissionExists {
1✔
145
        //        logging.Log().Debug("No presentation submission present in the request.")
1✔
146
        //        c.AbortWithStatusJSON(400, ErrorMessageNoPresentationSubmission)
1✔
147
        //        return
1✔
148
        //}
1✔
149

1✔
150
        scope, scopeExists := c.GetPostForm("scope")
1✔
151
        if !scopeExists {
2✔
152
                logging.Log().Debug("No scope present in the request.")
1✔
153
                c.AbortWithStatusJSON(400, ErrorMessageNoScope)
1✔
154
                return
1✔
155
        }
1✔
156

157
        presentation, err := extractVpFromToken(c, vpToken)
1✔
158
        if err != nil {
1✔
159
                logging.Log().Warnf("Was not able to extract the credentials from the vp_token. E: %v", err)
×
160
                return
×
161
        }
×
162

163
        scopes := strings.Split(scope, ",")
1✔
164

1✔
165
        // Subject is empty since multiple VCs with different subjects can be provided
1✔
166
        expiration, signedToken, err := getApiVerifier().GenerateToken(clientId, "", clientId, scopes, presentation)
1✔
167
        if err != nil {
1✔
168
                logging.Log().Error("Failure during generating M2M token: ", err)
×
169
                c.AbortWithStatusJSON(400, err)
×
170
                return
×
171
        }
×
172
        response := TokenResponse{"Bearer", float32(expiration), signedToken, requestBody.Scope, ""}
1✔
173
        logging.Log().Infof("Generated and signed token: %v", response)
1✔
174
        c.JSON(http.StatusOK, response)
1✔
175
}
176

177
func handleTokenTypeCode(c *gin.Context) {
1✔
178

1✔
179
        code, codeExists := c.GetPostForm("code")
1✔
180
        if !codeExists {
2✔
181
                logging.Log().Debug("No code present in the request.")
1✔
182
                c.AbortWithStatusJSON(400, ErrorMessageNoCode)
1✔
183
                return
1✔
184
        }
1✔
185

186
        assertionType, assertionTypeExists := c.GetPostForm("client_assertion_type")
1✔
187
        redirectUri, redirectUriExists := c.GetPostForm("redirect_uri")
1✔
188
        if redirectUriExists {
2✔
189
                jwt, expiration, err := getApiVerifier().GetToken(code, redirectUri, false)
1✔
190
                if err != nil {
2✔
191
                        c.AbortWithStatusJSON(403, ErrorMessage{Summary: err.Error()})
1✔
192
                        return
1✔
193
                }
1✔
194
                c.JSON(http.StatusOK, TokenResponse{TokenType: "Bearer", ExpiresIn: float32(expiration), AccessToken: jwt})
1✔
195
                return
1✔
196
        }
197
        if assertionTypeExists {
1✔
198
                handleWithClientAssertion(c, assertionType, code)
×
199
                return
×
200
        }
×
201
        c.AbortWithStatusJSON(400, ErrorMessageInvalidTokenRequest)
1✔
202
}
203

204
func handleWithClientAssertion(c *gin.Context, assertionType string, code string) {
×
205
        if assertionType != "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" {
×
206
                logging.Log().Warnf("Assertion type %s is not supported.", assertionType)
×
207
                c.AbortWithStatusJSON(400, ErrorMessageUnsupportedAssertionType)
×
208
                return
×
209
        }
×
210

211
        clientAssertion, clientAssertionExists := c.GetPostForm("client_assertion")
×
212
        clientId, clientIdExists := c.GetPostForm("client_id")
×
213
        if !clientAssertionExists || !clientIdExists {
×
214
                logging.Log().Warnf("Client Id (%s) or assertion (%s) not provided.", clientId, clientAssertion)
×
215
                c.AbortWithStatusJSON(400, ErrorMessageUnsupportedAssertionType)
×
216
                return
×
217
        }
×
218

219
        kid, err := getKeyResolver().ExtractKIDFromJWT(clientAssertion)
×
220
        if err != nil {
×
221
                logging.Log().Warnf("Was not able to retrive kid from token %s. Err: %v.", clientAssertion, err)
×
222
                c.AbortWithStatusJSON(400, ErrorMessageInvalidClientAssertion)
×
223
                return
×
224
        }
×
225
        pubKey, err := getKeyResolver().ResolvePublicKeyFromDID(kid)
×
226
        if err != nil {
×
227
                logging.Log().Warnf("Was not able to retrive key from kid %s. Err: %v.", kid, err)
×
228
                c.AbortWithStatusJSON(400, ErrorMessageInvalidClientAssertion)
×
229
                return
×
230
        }
×
231

232
        alg, algExists := pubKey.Algorithm()
×
233
        if !algExists {
×
234
                // fallback to default
×
235
                alg = jwa.ES256()
×
236
        }
×
237

238
        parsed, err := jwt.Parse([]byte(clientAssertion), jwt.WithKey(alg, pubKey))
×
239
        if err != nil {
×
240
                logging.Log().Warnf("Was not able to parse and verify the token %s. Err: %v", clientAssertion, err)
×
241
                c.AbortWithStatusJSON(400, ErrorMessageInvalidClientAssertion)
×
242
                return
×
243
        }
×
244

245
        // Serialize token to JSON
246
        jsonBytes, err := json.Marshal(parsed)
×
247
        if err != nil {
×
248
                logging.Log().Warnf("Was not able to marshal the token %s. Err: %v", clientAssertion, err)
×
249
                c.AbortWithStatusJSON(400, ErrorMessageInvalidClientAssertion)
×
250
                return
×
251
        }
×
252

253
        // Unmarshal to your struct
254
        var clientAssertionObject ClientAssertion
×
255
        if err := json.Unmarshal(jsonBytes, &clientAssertionObject); err != nil {
×
256
                logging.Log().Warnf("Was not able to unmarshal the token: %s, Err: %v", string(jsonBytes), err)
×
257
                c.AbortWithStatusJSON(400, ErrorMessageInvalidClientAssertion)
×
258
                return
×
259
        }
×
260

261
        if clientAssertionObject.Sub != clientId || clientAssertionObject.Iss != clientId || !slices.Contains(clientAssertionObject.Aud, getFrontendVerifier().GetHost()) {
×
262
                logging.Log().Warnf("Invalid assertion: %s. Client Id: %s, Host: %s", logging.PrettyPrintObject(clientAssertionObject), clientId, getApiVerifier().GetHost())
×
263
                c.AbortWithStatusJSON(400, ErrorMessageInvalidClientAssertion)
×
264
                return
×
265
        }
×
266

267
        jwt, expiration, err := getApiVerifier().GetToken(code, "", true)
×
268
        if err != nil {
×
269
                c.AbortWithStatusJSON(403, ErrorMessage{Summary: err.Error()})
×
270
                return
×
271
        }
×
272
        c.JSON(http.StatusOK, TokenResponse{TokenType: "Bearer", ExpiresIn: float32(expiration), AccessToken: jwt})
×
273
}
274

275
// StartSIOPSameDevice - Starts the siop flow for credentials hold by the same device
276
func StartSIOPSameDevice(c *gin.Context) {
1✔
277
        state, stateExists := c.GetQuery("state")
1✔
278
        if !stateExists {
2✔
279
                logging.Log().Debugf("No state was provided.")
1✔
280
                c.AbortWithStatusJSON(400, ErrorMessage{"no_state_provided", "Authentication requires a state provided as query parameter."})
1✔
281
                return
1✔
282
        }
1✔
283
        redirectPath, redirectPathExists := c.GetQuery("redirect_path")
1✔
284
        if !redirectPathExists {
2✔
285
                redirectPath = "/"
1✔
286
        }
1✔
287

288
        protocol := "https"
1✔
289
        if c.Request.TLS == nil {
2✔
290
                protocol = "http"
1✔
291
        }
1✔
292

293
        clientId, clientIdExists := c.GetQuery("client_id")
1✔
294
        logging.Log().Debugf("The client id %s", clientId)
1✔
295
        if !clientIdExists {
2✔
296
                logging.Log().Infof("Start a login flow for a not specified client.")
1✔
297
        }
1✔
298

299
        requestMode, requestModeExists := c.GetQuery("request_mode")
1✔
300
        if !requestModeExists {
2✔
301
                logging.Log().Infof("Using default request mode %s.", DEFAULT_REQUEST_MODE)
1✔
302
                requestMode = DEFAULT_REQUEST_MODE
1✔
303
        }
1✔
304

305
        redirect, err := getApiVerifier().StartSameDeviceFlow(c.Request.Host, protocol, state, redirectPath, clientId, requestMode)
1✔
306
        if err != nil {
2✔
307
                logging.Log().Warnf("Error starting the same-device flow. Err: %v", err)
1✔
308
                c.AbortWithStatusJSON(500, ErrorMessage{err.Error(), "Was not able to start the same device flow."})
1✔
309
                return
1✔
310
        }
1✔
311
        c.Redirect(302, redirect)
1✔
312
}
313

314
// VerifierAPIAuthenticationResponse - Stores the credential for the given session
315
func VerifierAPIAuthenticationResponse(c *gin.Context) {
1✔
316

1✔
317
        var state string
1✔
318
        stateForm, stateFormExists := c.GetPostForm("state")
1✔
319
        stateQuery, stateQueryExists := c.GetQuery("state")
1✔
320
        if !stateFormExists && !stateQueryExists {
2✔
321
                c.AbortWithStatusJSON(400, ErrorMessageNoState)
1✔
322
                return
1✔
323
        }
1✔
324
        if stateFormExists {
2✔
325
                state = stateForm
1✔
326
        } else {
1✔
327
                // allow the state submitted through a query parameter for backwards-compatibility
×
328
                state = stateQuery
×
329
        }
×
330

331
        vptoken, tokenExists := c.GetPostForm("vp_token")
1✔
332
        if !tokenExists {
2✔
333
                logging.Log().Info("No token was provided.")
1✔
334
                c.AbortWithStatusJSON(400, ErrorMessageNoToken)
1✔
335
                return
1✔
336
        }
1✔
337

338
        presentation, err := extractVpFromToken(c, vptoken)
1✔
339
        if err != nil {
2✔
340
                logging.Log().Warnf("Was not able to extract the presentation from the vp_token.")
1✔
341
                return
1✔
342
        }
1✔
343
        handleAuthenticationResponse(c, state, presentation)
1✔
344
}
345

346
// GetVerifierAPIAuthenticationResponse - Stores the credential for the given session
347
func GetVerifierAPIAuthenticationResponse(c *gin.Context) {
×
348
        state, stateExists := c.GetQuery("state")
×
349
        if !stateExists {
×
350
                c.AbortWithStatusJSON(400, ErrorMessageNoState)
×
351
                return
×
352
        }
×
353

354
        vpToken, tokenExists := c.GetQuery("vp_token")
×
355
        if !tokenExists {
×
356
                logging.Log().Info("No token was provided.")
×
357
                c.AbortWithStatusJSON(400, ErrorMessageNoToken)
×
358
                return
×
359
        }
×
360
        presentation, err := extractVpFromToken(c, vpToken)
×
361
        if err != nil {
×
362
                logging.Log().Warnf("Was not able to extract the presentation from the vp_token.")
×
363
                return
×
364
        }
×
365
        handleAuthenticationResponse(c, state, presentation)
×
366
}
367

368
// GetRequestByReference - Get the request object by reference
369
func GetRequestByReference(c *gin.Context) {
×
370
        sessionId := c.Param("id")
×
371

×
372
        jwt, err := verifier.GetVerifier().GetRequestObject(sessionId)
×
373
        if err != nil {
×
374
                logging.Log().Debugf("No request for  %s. Err: %v", sessionId, err)
×
375
                c.AbortWithStatusJSON(404, ErrorMessageNoSuchSession)
×
376
                return
×
377
        }
×
378
        c.String(http.StatusOK, jwt)
×
379
}
380

381
func extractVpFromToken(c *gin.Context, vpToken string) (parsedPresentation *verifiable.Presentation, err error) {
1✔
382

1✔
383
        logging.Log().Debugf("The token %s.", vpToken)
1✔
384

1✔
385
        parsedPresentation, err = getPresentationFromQuery(c, vpToken)
1✔
386

1✔
387
        if err != nil {
1✔
NEW
388
                logging.Log().Debugf("Received a vpToken with a query, but was not able to extract the presentation. Token: %s", vpToken)
×
NEW
389
                return parsedPresentation, err
×
NEW
390
        }
×
391
        if parsedPresentation != nil {
1✔
NEW
392
                return parsedPresentation, err
×
NEW
393
        }
×
394
        return tokenToPresentation(c, vpToken)
1✔
395

396
}
397

398
func tokenToPresentation(c *gin.Context, vpToken string) (parsedPresentation *verifiable.Presentation, err error) {
1✔
399
        tokenBytes := decodeVpString(vpToken)
1✔
400

1✔
401
        isSdJWT, parsedPresentation, err := isSdJWT(c, vpToken)
1✔
402
        if isSdJWT && err != nil {
1✔
403
                return
×
404
        }
×
405
        if isSdJWT {
2✔
406
                logging.Log().Debugf("Received an sdJwt: %s", logging.PrettyPrintObject(parsedPresentation))
1✔
407
                return
1✔
408
        }
1✔
409

410
        parsedPresentation, err = getPresentationParser().ParsePresentation(tokenBytes)
1✔
411

1✔
412
        if err != nil {
2✔
413
                logging.Log().Infof("Was not able to parse the token %s. Err: %v", vpToken, err)
1✔
414
                c.AbortWithStatusJSON(400, ErrorMessageUnableToDecodeToken)
1✔
415
                return
1✔
416
        }
1✔
417
        return
1✔
418
}
419

420
func getPresentationFromQuery(c *gin.Context, vpToken string) (parsedPresentation *verifiable.Presentation, err error) {
1✔
421
        tokenBytes := decodeVpString(vpToken)
1✔
422

1✔
423
        var queryMap map[string]string
1✔
424
        //unmarshal
1✔
425
        err = json.Unmarshal(tokenBytes, &queryMap)
1✔
426
        if err != nil {
2✔
427
                logging.Log().Debug("VP Token does not contain query map. Checking the other options.", err)
1✔
428
                return nil, nil
1✔
429
        }
1✔
430

NEW
431
        for _, v := range queryMap {
×
NEW
432
                p, err := tokenToPresentation(c, v)
×
NEW
433
                if err != nil {
×
NEW
434
                        return nil, err
×
NEW
435
                }
×
NEW
436
                if parsedPresentation == nil {
×
NEW
437
                        parsedPresentation = p
×
NEW
438
                } else {
×
NEW
439
                        parsedPresentation.AddCredentials(p.Credentials()...)
×
NEW
440
                }
×
441
        }
NEW
442
        return parsedPresentation, err
×
443
}
444

445
// checks if the presented token contains a single sd-jwt credential. Will be repackage to a presentation for further validation
446
func isSdJWT(c *gin.Context, vpToken string) (isSdJwt bool, presentation *verifiable.Presentation, err error) {
1✔
447
        claims, err := getSdJwtParser().Parse(vpToken)
1✔
448
        if err != nil {
2✔
449
                logging.Log().Debugf("Was not a sdjwt. Err: %v", err)
1✔
450
                return false, presentation, err
1✔
451
        }
1✔
452
        issuer, i_ok := claims["iss"]
1✔
453
        vct, vct_ok := claims["vct"]
1✔
454
        if !i_ok || !vct_ok {
1✔
455
                logging.Log().Infof("Token does not contain issuer(%v) or vct(%v).", i_ok, vct_ok)
×
456
                c.AbortWithStatusJSON(400, ErrorMessageInvalidSdJwt)
×
457
                return true, presentation, errors.New(ErrorMessageInvalidSdJwt.Summary)
×
458
        }
×
459
        customFields := verifiable.CustomFields{}
1✔
460
        for k, v := range claims {
2✔
461
                if k != "iss" && k != "vct" {
2✔
462
                        customFields[k] = v
1✔
463
                }
1✔
464
        }
465
        subject := verifiable.Subject{CustomFields: customFields}
1✔
466
        contents := verifiable.CredentialContents{Issuer: &verifiable.Issuer{ID: issuer.(string)}, Types: []string{vct.(string)}, Subject: []verifiable.Subject{subject}}
1✔
467
        credential, err := verifiable.CreateCredential(contents, verifiable.CustomFields{})
1✔
468
        if err != nil {
1✔
469
                logging.Log().Infof("Was not able to create credential from sdJwt. E: %v", err)
×
470
                c.AbortWithStatusJSON(400, ErrorMessageInvalidSdJwt)
×
471
                return true, presentation, err
×
472
        }
×
473
        presentation, err = verifiable.NewPresentation()
1✔
474
        if err != nil {
1✔
475
                logging.Log().Infof("Was not able to create credpresentation from sdJwt. E: %v", err)
×
476
                c.AbortWithStatusJSON(400, ErrorMessageInvalidSdJwt)
×
477
                return true, presentation, err
×
478
        }
×
479
        presentation.AddCredentials(credential)
1✔
480
        presentation.Holder = issuer.(string)
1✔
481
        return true, presentation, nil
1✔
482
}
483

484
// decodeVpString - In newer versions of OID4VP the token is not encoded as a whole but only its segments separately. This function covers the older and newer versions
485
func decodeVpString(vpToken string) (tokenBytes []byte) {
1✔
486
        tokenBytes, err := base64.RawURLEncoding.DecodeString(vpToken)
1✔
487
        if err != nil {
2✔
488
                return []byte(vpToken)
1✔
489
        }
1✔
490
        return tokenBytes
1✔
491
}
492

493
func handleAuthenticationResponse(c *gin.Context, state string, presentation *verifiable.Presentation) {
1✔
494

1✔
495
        response, err := getApiVerifier().AuthenticationResponse(state, presentation)
1✔
496
        if err != nil {
2✔
497
                logging.Log().Warnf("Was not able to fullfil the authentication response. Err: %v", err)
1✔
498
                c.AbortWithStatusJSON(400, ErrorMessage{Summary: err.Error()})
1✔
499
                return
1✔
500
        }
1✔
501
        if response != (verifier.Response{}) && response.FlowVersion == verifier.SAME_DEVICE {
2✔
502
                if response.Nonce != "" {
1✔
503
                        c.Redirect(302, fmt.Sprintf("%s?state=%s&code=%s&nonce=%s", response.RedirectTarget, response.SessionId, response.Code, response.Nonce))
×
504
                } else {
1✔
505
                        c.Redirect(302, fmt.Sprintf("%s?state=%s&code=%s", response.RedirectTarget, response.SessionId, response.Code))
1✔
506
                }
1✔
507
                return
1✔
508
        } else if response != (verifier.Response{}) && response.FlowVersion == verifier.CROSS_DEVICE_V2 {
×
509
                sendRedirect(c, response.SessionId, response.Code, response.RedirectTarget)
×
510
        }
×
511
        logging.Log().Debugf("Successfully authenticated %s.", state)
×
512
        c.JSON(http.StatusOK, gin.H{})
×
513
}
514

515
// VerifierAPIJWKS - Provides the public keys for the given verifier, to be used for verifing the JWTs
516
func VerifierAPIJWKS(c *gin.Context) {
×
517
        c.JSON(http.StatusOK, getApiVerifier().GetJWKS())
×
518
}
×
519

520
// VerifierAPIOpenID
521
func VerifierAPIOpenIDConfiguration(c *gin.Context) {
×
522

×
523
        metadata, err := getApiVerifier().GetOpenIDConfiguration(c.Param("service_id"))
×
524
        if err != nil {
×
525
                c.AbortWithStatusJSON(500, ErrorMessage{err.Error(), "Was not able to generate the OpenID metadata."})
×
526
                return
×
527
        }
×
528
        c.JSON(http.StatusOK, metadata)
×
529
}
530

531
// VerifierAPIStartSIOP - Initiates the siop flow and returns the 'openid://...' connection string
532
func VerifierAPIStartSIOP(c *gin.Context) {
1✔
533
        state, stateExists := c.GetQuery("state")
1✔
534
        if !stateExists {
2✔
535
                c.AbortWithStatusJSON(400, ErrorMessageNoState)
1✔
536
                // early exit
1✔
537
                return
1✔
538
        }
1✔
539

540
        callback, callbackExists := c.GetQuery("client_callback")
1✔
541
        if !callbackExists {
2✔
542
                c.AbortWithStatusJSON(400, ErrorMessageNoCallback)
1✔
543
                // early exit
1✔
544
                return
1✔
545
        }
1✔
546
        protocol := "https"
1✔
547
        if c.Request.TLS == nil {
2✔
548
                protocol = "http"
1✔
549
        }
1✔
550
        clientId, clientIdExists := c.GetQuery("client_id")
1✔
551
        if !clientIdExists {
2✔
552
                logging.Log().Infof("Start a login flow for a not specified client.")
1✔
553
        }
1✔
554

555
        requestMode, requestModeExists := c.GetQuery("request_mode")
1✔
556
        if !requestModeExists {
2✔
557
                logging.Log().Infof("Using default request mode %s.", DEFAULT_REQUEST_MODE)
1✔
558
                requestMode = DEFAULT_REQUEST_MODE
1✔
559
        }
1✔
560

561
        connectionString, err := getApiVerifier().StartSiopFlow(c.Request.Host, protocol, callback, state, clientId, "", requestMode)
1✔
562
        if err != nil {
2✔
563
                c.AbortWithStatusJSON(500, ErrorMessage{err.Error(), "Was not able to generate the connection string."})
1✔
564
                return
1✔
565
        }
1✔
566
        c.String(http.StatusOK, connectionString)
1✔
567
}
568

569
type ClientAssertion struct {
570
        Iss string   `json:"iss"`
571
        Aud []string `json:"aud"`
572
        Sub string   `json:"sub"`
573
        Exp int      `json:"exp"`
574
        Iat int      `json:"iat"`
575
}
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