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

FIWARE / VCVerifier / 15674278844

16 Jun 2025 07:13AM UTC coverage: 37.558% (-7.8%) from 45.372%
15674278844

push

github

web-flow
Merge pull request #58 from FIWARE/jwt-inclusion

Jwt inclusion

99 of 674 new or added lines in 13 files covered. (14.69%)

14 existing lines in 3 files now uncovered.

1215 of 3235 relevant lines covered (37.56%)

0.42 hits per line

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

59.28
/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✔
NEW
78
                sdJwtParser = verifier.GetSdJwtParser()
×
NEW
79
        }
×
80
        return sdJwtParser
1✔
81
}
82

NEW
83
func getKeyResolver() verifier.KeyResolver {
×
NEW
84
        if keyResolver == nil {
×
NEW
85
                keyResolver = &verifier.VdrKeyResolver{Vdr: []api.VDR{vdr_key.New(), vdr_jwk.New(), vdr_web.New()}}
×
NEW
86
        }
×
NEW
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✔
NEW
198
                handleWithClientAssertion(c, assertionType, code)
×
NEW
199
                return
×
NEW
200
        }
×
201
        c.AbortWithStatusJSON(400, ErrorMessageInvalidTokenRequest)
1✔
202
}
203

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

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

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

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

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

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

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

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

NEW
267
        jwt, expiration, err := getApiVerifier().GetToken(code, "", true)
×
NEW
268
        if err != nil {
×
NEW
269
                c.AbortWithStatusJSON(403, ErrorMessage{Summary: err.Error()})
×
NEW
270
                return
×
NEW
271
        }
×
UNCOV
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
        if !clientIdExists {
2✔
295
                logging.Log().Infof("Start a login flow for a not specified client.")
1✔
296
        }
1✔
297

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

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

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

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

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

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

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

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

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

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

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

1✔
382
        tokenBytes := decodeVpString(vpToken)
1✔
383

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

1✔
386
        isSdJWT, parsedPresentation, err := isSdJWT(c, vpToken)
1✔
387
        if isSdJWT && err != nil {
1✔
NEW
388
                return
×
NEW
389
        }
×
390
        if isSdJWT {
2✔
391
                logging.Log().Debugf("Received an sdJwt: %s", logging.PrettyPrintObject(parsedPresentation))
1✔
392
                return
1✔
393
        }
1✔
394

395
        parsedPresentation, err = getPresentationParser().ParsePresentation(tokenBytes)
1✔
396

1✔
397
        if err != nil {
2✔
398
                logging.Log().Infof("Was not able to parse the token %s. Err: %v", vpToken, err)
1✔
399
                c.AbortWithStatusJSON(400, ErrorMessageUnableToDecodeToken)
1✔
400
                return
1✔
401
        }
1✔
402
        return
1✔
403
}
404

405
// checks if the presented token contains a single sd-jwt credential. Will be repackage to a presentation for further validation
406
func isSdJWT(c *gin.Context, vpToken string) (isSdJwt bool, presentation *verifiable.Presentation, err error) {
1✔
407
        claims, err := getSdJwtParser().Parse(vpToken)
1✔
408
        if err != nil {
2✔
409
                logging.Log().Debugf("Was not a sdjwt. Err: %v", err)
1✔
410
                return false, presentation, err
1✔
411
        }
1✔
412
        issuer, i_ok := claims["iss"]
1✔
413
        vct, vct_ok := claims["vct"]
1✔
414
        if !i_ok || !vct_ok {
1✔
NEW
415
                logging.Log().Infof("Token does not contain issuer(%v) or vct(%v).", i_ok, vct_ok)
×
NEW
416
                c.AbortWithStatusJSON(400, ErrorMessageInvalidSdJwt)
×
NEW
417
                return true, presentation, errors.New(ErrorMessageInvalidSdJwt.Summary)
×
NEW
418
        }
×
419
        customFields := verifiable.CustomFields{}
1✔
420
        for k, v := range claims {
2✔
421
                if k != "iss" && k != "vct" {
2✔
422
                        customFields[k] = v
1✔
423
                }
1✔
424
        }
425
        subject := verifiable.Subject{CustomFields: customFields}
1✔
426
        contents := verifiable.CredentialContents{Issuer: &verifiable.Issuer{ID: issuer.(string)}, Types: []string{vct.(string)}, Subject: []verifiable.Subject{subject}}
1✔
427
        credential, err := verifiable.CreateCredential(contents, verifiable.CustomFields{})
1✔
428
        if err != nil {
1✔
NEW
429
                logging.Log().Infof("Was not able to create credential from sdJwt. E: %v", err)
×
NEW
430
                c.AbortWithStatusJSON(400, ErrorMessageInvalidSdJwt)
×
NEW
431
                return true, presentation, err
×
NEW
432
        }
×
433
        presentation, err = verifiable.NewPresentation()
1✔
434
        if err != nil {
1✔
NEW
435
                logging.Log().Infof("Was not able to create credpresentation from sdJwt. E: %v", err)
×
NEW
436
                c.AbortWithStatusJSON(400, ErrorMessageInvalidSdJwt)
×
NEW
437
                return true, presentation, err
×
NEW
438
        }
×
439
        presentation.AddCredentials(credential)
1✔
440
        presentation.Holder = issuer.(string)
1✔
441
        return true, presentation, nil
1✔
442
}
443

444
// 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
445
func decodeVpString(vpToken string) (tokenBytes []byte) {
1✔
446
        tokenBytes, err := base64.RawURLEncoding.DecodeString(vpToken)
1✔
447
        if err != nil {
2✔
448
                return []byte(vpToken)
1✔
449
        }
1✔
450
        return tokenBytes
1✔
451
}
452

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

1✔
455
        response, err := getApiVerifier().AuthenticationResponse(state, presentation)
1✔
456
        if err != nil {
2✔
457
                logging.Log().Warnf("Was not able to fullfil the authentication response. Err: %v", err)
1✔
458
                c.AbortWithStatusJSON(400, ErrorMessage{Summary: err.Error()})
1✔
459
                return
1✔
460
        }
1✔
461
        if response != (verifier.Response{}) && response.FlowVersion == verifier.SAME_DEVICE {
2✔
462
                if response.Nonce != "" {
1✔
NEW
463
                        c.Redirect(302, fmt.Sprintf("%s?state=%s&code=%s&nonce=%s", response.RedirectTarget, response.SessionId, response.Code, response.Nonce))
×
464
                } else {
1✔
465
                        c.Redirect(302, fmt.Sprintf("%s?state=%s&code=%s", response.RedirectTarget, response.SessionId, response.Code))
1✔
466
                }
1✔
467
                return
1✔
NEW
468
        } else if response != (verifier.Response{}) && response.FlowVersion == verifier.CROSS_DEVICE_V2 {
×
NEW
469
                sendRedirect(c, response.SessionId, response.Code, response.RedirectTarget)
×
UNCOV
470
        }
×
UNCOV
471
        logging.Log().Debugf("Successfully authenticated %s.", state)
×
UNCOV
472
        c.JSON(http.StatusOK, gin.H{})
×
473
}
474

475
// VerifierAPIJWKS - Provides the public keys for the given verifier, to be used for verifing the JWTs
476
func VerifierAPIJWKS(c *gin.Context) {
×
477
        c.JSON(http.StatusOK, getApiVerifier().GetJWKS())
×
478
}
×
479

480
// VerifierAPIOpenID
481
func VerifierAPIOpenIDConfiguration(c *gin.Context) {
×
482

×
483
        metadata, err := getApiVerifier().GetOpenIDConfiguration(c.Param("service_id"))
×
484
        if err != nil {
×
485
                c.AbortWithStatusJSON(500, ErrorMessage{err.Error(), "Was not able to generate the OpenID metadata."})
×
486
                return
×
487
        }
×
488
        c.JSON(http.StatusOK, metadata)
×
489
}
490

491
// VerifierAPIStartSIOP - Initiates the siop flow and returns the 'openid://...' connection string
492
func VerifierAPIStartSIOP(c *gin.Context) {
1✔
493
        state, stateExists := c.GetQuery("state")
1✔
494
        if !stateExists {
2✔
495
                c.AbortWithStatusJSON(400, ErrorMessageNoState)
1✔
496
                // early exit
1✔
497
                return
1✔
498
        }
1✔
499

500
        callback, callbackExists := c.GetQuery("client_callback")
1✔
501
        if !callbackExists {
2✔
502
                c.AbortWithStatusJSON(400, ErrorMessageNoCallback)
1✔
503
                // early exit
1✔
504
                return
1✔
505
        }
1✔
506
        protocol := "https"
1✔
507
        if c.Request.TLS == nil {
2✔
508
                protocol = "http"
1✔
509
        }
1✔
510
        clientId, clientIdExists := c.GetQuery("client_id")
1✔
511
        if !clientIdExists {
2✔
512
                logging.Log().Infof("Start a login flow for a not specified client.")
1✔
513
        }
1✔
514

515
        requestMode, requestModeExists := c.GetQuery("request_mode")
1✔
516
        if !requestModeExists {
2✔
517
                logging.Log().Infof("Using default request mode %s.", DEFAULT_REQUEST_MODE)
1✔
518
                requestMode = DEFAULT_REQUEST_MODE
1✔
519
        }
1✔
520

521
        connectionString, err := getApiVerifier().StartSiopFlow(c.Request.Host, protocol, callback, state, clientId, requestMode)
1✔
522
        if err != nil {
2✔
523
                c.AbortWithStatusJSON(500, ErrorMessage{err.Error(), "Was not able to generate the connection string."})
1✔
524
                return
1✔
525
        }
1✔
526
        c.String(http.StatusOK, connectionString)
1✔
527
}
528

529
type ClientAssertion struct {
530
        Iss string   `json:"iss"`
531
        Aud []string `json:"aud"`
532
        Sub string   `json:"sub"`
533
        Exp int      `json:"exp"`
534
        Iat int      `json:"iat"`
535
}
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