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

FIWARE / VCVerifier / 13648664348

04 Mar 2025 07:42AM UTC coverage: 45.006% (+0.02%) from 44.983%
13648664348

push

github

web-flow
Merge pull request #50 from FIWARE/extend-logging

logging

1 of 2 new or added lines in 2 files covered. (50.0%)

1050 of 2333 relevant lines covered (45.01%)

0.51 hits per line

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

73.97
/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
        "fmt"
15
        "net/http"
16
        "strings"
17

18
        "github.com/fiware/VCVerifier/common"
19
        "github.com/fiware/VCVerifier/logging"
20
        "github.com/fiware/VCVerifier/verifier"
21
        "github.com/trustbloc/vc-go/verifiable"
22

23
        "github.com/gin-gonic/gin"
24
)
25

26
var apiVerifier verifier.Verifier
27
var presentationParser verifier.PresentationParser
28

29
var ErrorMessagNoGrantType = ErrorMessage{"no_grant_type_provided", "Token requests require a grant_type."}
30
var ErrorMessageUnsupportedGrantType = ErrorMessage{"unsupported_grant_type", "Provided grant_type is not supported by the implementation."}
31
var ErrorMessageNoCode = ErrorMessage{"no_code_provided", "Token requests require a code."}
32
var ErrorMessageNoRedircetUri = ErrorMessage{"no_redirect_uri_provided", "Token requests require a redirect_uri."}
33
var ErrorMessageNoState = ErrorMessage{"no_state_provided", "Authentication requires a state provided as query parameter."}
34
var ErrorMessageNoScope = ErrorMessage{"no_scope_provided", "Authentication requires a scope provided as a form parameter."}
35
var ErrorMessageNoToken = ErrorMessage{"no_token_provided", "Authentication requires a token provided as a form parameter."}
36
var ErrorMessageNoPresentationSubmission = ErrorMessage{"no_presentation_submission_provided", "Authentication requires a presentation submission provided as a form parameter."}
37
var ErrorMessageNoCallback = ErrorMessage{"NoCallbackProvided", "A callback address has to be provided as query-parameter."}
38
var ErrorMessageUnableToDecodeToken = ErrorMessage{"invalid_token", "Token could not be decoded."}
39
var ErrorMessageUnableToDecodeCredential = ErrorMessage{"invalid_token", "Could not read the credential(s) inside the token."}
40
var ErrorMessageUnableToDecodeHolder = ErrorMessage{"invalid_token", "Could not read the holder inside the token."}
41

42
func getApiVerifier() verifier.Verifier {
1✔
43
        if apiVerifier == nil {
1✔
44
                apiVerifier = verifier.GetVerifier()
×
45
        }
×
46
        return apiVerifier
1✔
47
}
48

49
func getPresentationParser() verifier.PresentationParser {
1✔
50
        if presentationParser == nil {
1✔
51
                presentationParser = verifier.GetPresentationParser()
×
52
        }
×
53
        return presentationParser
1✔
54
}
55

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

1✔
59
        logging.Log().Debugf("%v", c.Request)
1✔
60
        grantType, grantTypeExists := c.GetPostForm("grant_type")
1✔
61
        if !grantTypeExists {
2✔
62
                logging.Log().Debug("No grant_type present in the request.")
1✔
63
                c.AbortWithStatusJSON(400, ErrorMessagNoGrantType)
1✔
64
                return
1✔
65
        }
1✔
66

67
        if grantType == common.TYPE_CODE {
2✔
68
                handleTokenTypeCode(c)
1✔
69
        } else if grantType == common.TYPE_VP_TOKEN {
3✔
70
                handleTokenTypeVPToken(c, c.GetHeader("client_id"))
1✔
71
        } else {
2✔
72
                c.AbortWithStatusJSON(400, ErrorMessageUnsupportedGrantType)
1✔
73
        }
1✔
74
}
75

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

×
79
        logging.Log().Debugf("%v", c.Request)
×
80
        grantType, grantTypeExists := c.GetPostForm("grant_type")
×
81
        if !grantTypeExists {
×
82
                logging.Log().Debug("No grant_type present in the request.")
×
83
                c.AbortWithStatusJSON(400, ErrorMessagNoGrantType)
×
84
                return
×
85
        }
×
86

87
        if grantType == common.TYPE_CODE {
×
88
                handleTokenTypeCode(c)
×
89
        } else if grantType == common.TYPE_VP_TOKEN {
×
90
                handleTokenTypeVPToken(c, c.Param("service_id"))
×
91
        } else {
×
92
                c.AbortWithStatusJSON(400, ErrorMessageUnsupportedGrantType)
×
93
        }
×
94
}
95

96
func handleTokenTypeVPToken(c *gin.Context, clientId string) {
1✔
97
        var requestBody TokenRequestBody
1✔
98

1✔
99
        vpToken, vpTokenExists := c.GetPostForm("vp_token")
1✔
100
        if !vpTokenExists {
2✔
101
                logging.Log().Debug("No vp token present in the request.")
1✔
102
                c.AbortWithStatusJSON(400, ErrorMessageNoToken)
1✔
103
                return
1✔
104
        }
1✔
105

106
        logging.Log().Warnf("Got token %s", vpToken)
1✔
107

1✔
108
        // not used at the moment
1✔
109
        // presentationSubmission, presentationSubmissionExists := c.GetPostForm("presentation_submission")
1✔
110
        // if !presentationSubmissionExists {
1✔
111
        //        logging.Log().Debug("No presentation submission present in the request.")
1✔
112
        //        c.AbortWithStatusJSON(400, ErrorMessageNoPresentationSubmission)
1✔
113
        //        return
1✔
114
        //}
1✔
115

1✔
116
        scope, scopeExists := c.GetPostForm("scope")
1✔
117
        if !scopeExists {
2✔
118
                logging.Log().Debug("No scope present in the request.")
1✔
119
                c.AbortWithStatusJSON(400, ErrorMessageNoScope)
1✔
120
                return
1✔
121
        }
1✔
122

123
        presentation, err := extractVpFromToken(c, vpToken)
1✔
124
        if err != nil {
1✔
NEW
125
                logging.Log().Warnf("Was not able to extract the credentials from the vp_token. E: %v", err)
×
126
                return
×
127
        }
×
128

129
        scopes := strings.Split(scope, ",")
1✔
130

1✔
131
        // Subject is empty since multiple VCs with different subjects can be provided
1✔
132
        expiration, signedToken, err := getApiVerifier().GenerateToken(clientId, "", clientId, scopes, presentation)
1✔
133
        if err != nil {
1✔
134
                logging.Log().Error("Failure during generating M2M token: ", err)
×
135
                c.AbortWithStatusJSON(400, err)
×
136
                return
×
137
        }
×
138
        response := TokenResponse{"Bearer", float32(expiration), signedToken, requestBody.Scope, ""}
1✔
139
        logging.Log().Infof("Generated and signed token: %v", response)
1✔
140
        c.JSON(http.StatusOK, response)
1✔
141
}
142

143
func handleTokenTypeCode(c *gin.Context) {
1✔
144

1✔
145
        code, codeExists := c.GetPostForm("code")
1✔
146
        if !codeExists {
2✔
147
                logging.Log().Debug("No code present in the request.")
1✔
148
                c.AbortWithStatusJSON(400, ErrorMessageNoCode)
1✔
149
                return
1✔
150
        }
1✔
151

152
        redirectUri, redirectUriExists := c.GetPostForm("redirect_uri")
1✔
153
        if !redirectUriExists {
2✔
154
                logging.Log().Debug("No redircet_uri present in the request.")
1✔
155
                c.AbortWithStatusJSON(400, ErrorMessageNoRedircetUri)
1✔
156
                return
1✔
157
        }
1✔
158
        jwt, expiration, err := getApiVerifier().GetToken(code, redirectUri)
1✔
159

1✔
160
        if err != nil {
2✔
161
                c.AbortWithStatusJSON(403, ErrorMessage{Summary: err.Error()})
1✔
162
                return
1✔
163
        }
1✔
164

165
        c.JSON(http.StatusOK, TokenResponse{TokenType: "Bearer", ExpiresIn: float32(expiration), AccessToken: jwt})
1✔
166
}
167

168
// StartSIOPSameDevice - Starts the siop flow for credentials hold by the same device
169
func StartSIOPSameDevice(c *gin.Context) {
1✔
170
        state, stateExists := c.GetQuery("state")
1✔
171
        if !stateExists {
2✔
172
                logging.Log().Debugf("No state was provided.")
1✔
173
                c.AbortWithStatusJSON(400, ErrorMessage{"no_state_provided", "Authentication requires a state provided as query parameter."})
1✔
174
                return
1✔
175
        }
1✔
176
        redirectPath, redirectPathExists := c.GetQuery("redirect_path")
1✔
177
        if !redirectPathExists {
2✔
178
                redirectPath = "/"
1✔
179
        }
1✔
180

181
        protocol := "https"
1✔
182
        if c.Request.TLS == nil {
2✔
183
                protocol = "http"
1✔
184
        }
1✔
185

186
        clientId, clientIdExists := c.GetQuery("client_id")
1✔
187
        if !clientIdExists {
2✔
188
                logging.Log().Infof("Start a login flow for a not specified client.")
1✔
189
        }
1✔
190

191
        redirect, err := getApiVerifier().StartSameDeviceFlow(c.Request.Host, protocol, state, redirectPath, clientId)
1✔
192
        if err != nil {
2✔
193
                logging.Log().Warnf("Error starting the same-device flow. Err: %v", err)
1✔
194
                c.AbortWithStatusJSON(500, ErrorMessage{err.Error(), "Was not able to start the same device flow."})
1✔
195
                return
1✔
196
        }
1✔
197
        c.Redirect(302, redirect)
1✔
198
}
199

200
// VerifierAPIAuthenticationResponse - Stores the credential for the given session
201
func VerifierAPIAuthenticationResponse(c *gin.Context) {
1✔
202
        var state string
1✔
203
        stateForm, stateFormExists := c.GetPostForm("state")
1✔
204
        stateQuery, stateQueryExists := c.GetQuery("state")
1✔
205
        if !stateFormExists && !stateQueryExists {
2✔
206
                c.AbortWithStatusJSON(400, ErrorMessageNoState)
1✔
207
                return
1✔
208
        }
1✔
209
        if stateFormExists {
2✔
210
                state = stateForm
1✔
211
        } else {
1✔
212
                // allow the state submitted through a query parameter for backwards-compatibility
×
213
                state = stateQuery
×
214
        }
×
215

216
        vptoken, tokenExists := c.GetPostForm("vp_token")
1✔
217
        if !tokenExists {
2✔
218
                logging.Log().Info("No token was provided.")
1✔
219
                c.AbortWithStatusJSON(400, ErrorMessageNoToken)
1✔
220
                return
1✔
221
        }
1✔
222

223
        presentation, err := extractVpFromToken(c, vptoken)
1✔
224
        if err != nil {
2✔
225
                logging.Log().Warnf("Was not able to extract the presentation from the vp_token.")
1✔
226
                return
1✔
227
        }
1✔
228
        handleAuthenticationResponse(c, state, presentation)
1✔
229
}
230

231
// GetVerifierAPIAuthenticationResponse - Stores the credential for the given session
232
func GetVerifierAPIAuthenticationResponse(c *gin.Context) {
×
233
        state, stateExists := c.GetQuery("state")
×
234
        if !stateExists {
×
235
                c.AbortWithStatusJSON(400, ErrorMessageNoState)
×
236
                return
×
237
        }
×
238
        vpToken, tokenExists := c.GetQuery("vp_token")
×
239
        if !tokenExists {
×
240
                logging.Log().Info("No token was provided.")
×
241
                c.AbortWithStatusJSON(400, ErrorMessageNoToken)
×
242
                return
×
243
        }
×
244
        presentation, err := extractVpFromToken(c, vpToken)
×
245
        if err != nil {
×
246
                logging.Log().Warnf("Was not able to extract the presentation from the vp_token.")
×
247
                return
×
248
        }
×
249
        handleAuthenticationResponse(c, state, presentation)
×
250
}
251

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

1✔
254
        tokenBytes, err := base64.RawURLEncoding.DecodeString(vpToken)
1✔
255
        if err != nil {
1✔
256
                logging.Log().Infof("Was not able to decode the form string %s. Err: %v", vpToken, err)
×
257
                c.AbortWithStatusJSON(400, ErrorMessageUnableToDecodeToken)
×
258
                return
×
259
        }
×
260

261
        parsedPresentation, err = getPresentationParser().ParsePresentation(tokenBytes)
1✔
262
        if err != nil {
2✔
263
                logging.Log().Infof("Was not able to parse the token %s. Err: %v", vpToken, err)
1✔
264
                c.AbortWithStatusJSON(400, ErrorMessageUnableToDecodeToken)
1✔
265
                return
1✔
266
        }
1✔
267
        return
1✔
268
}
269

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

1✔
272
        sameDeviceResponse, err := getApiVerifier().AuthenticationResponse(state, presentation)
1✔
273
        if err != nil {
2✔
274
                logging.Log().Warnf("Was not able to fullfil the authentication response. Err: %v", err)
1✔
275
                c.AbortWithStatusJSON(400, ErrorMessage{Summary: err.Error()})
1✔
276
                return
1✔
277
        }
1✔
278
        if sameDeviceResponse != (verifier.SameDeviceResponse{}) {
2✔
279
                c.Redirect(302, fmt.Sprintf("%s?state=%s&code=%s", sameDeviceResponse.RedirectTarget, sameDeviceResponse.SessionId, sameDeviceResponse.Code))
1✔
280
                return
1✔
281
        }
1✔
282
        logging.Log().Debugf("Successfully authenticated %s.", state)
1✔
283
        c.JSON(http.StatusOK, gin.H{})
1✔
284
}
285

286
// VerifierAPIJWKS - Provides the public keys for the given verifier, to be used for verifing the JWTs
287
func VerifierAPIJWKS(c *gin.Context) {
×
288
        c.JSON(http.StatusOK, getApiVerifier().GetJWKS())
×
289
}
×
290

291
// VerifierAPIOpenID
292
func VerifierAPIOpenIDConfiguration(c *gin.Context) {
×
293

×
294
        metadata, err := getApiVerifier().GetOpenIDConfiguration(c.Param("service_id"))
×
295
        if err != nil {
×
296
                c.AbortWithStatusJSON(500, ErrorMessage{err.Error(), "Was not able to generate the OpenID metadata."})
×
297
                return
×
298
        }
×
299
        c.JSON(http.StatusOK, metadata)
×
300
}
301

302
// VerifierAPIStartSIOP - Initiates the siop flow and returns the 'openid://...' connection string
303
func VerifierAPIStartSIOP(c *gin.Context) {
1✔
304
        state, stateExists := c.GetQuery("state")
1✔
305
        if !stateExists {
2✔
306
                c.AbortWithStatusJSON(400, ErrorMessageNoState)
1✔
307
                // early exit
1✔
308
                return
1✔
309
        }
1✔
310

311
        callback, callbackExists := c.GetQuery("client_callback")
1✔
312
        if !callbackExists {
2✔
313
                c.AbortWithStatusJSON(400, ErrorMessageNoCallback)
1✔
314
                // early exit
1✔
315
                return
1✔
316
        }
1✔
317
        protocol := "https"
1✔
318
        if c.Request.TLS == nil {
2✔
319
                protocol = "http"
1✔
320
        }
1✔
321
        clientId, clientIdExists := c.GetQuery("client_id")
1✔
322
        if !clientIdExists {
2✔
323
                logging.Log().Infof("Start a login flow for a not specified client.")
1✔
324
        }
1✔
325

326
        connectionString, err := getApiVerifier().StartSiopFlow(c.Request.Host, protocol, callback, state, clientId)
1✔
327
        if err != nil {
2✔
328
                c.AbortWithStatusJSON(500, ErrorMessage{err.Error(), "Was not able to generate the connection string."})
1✔
329
                return
1✔
330
        }
1✔
331
        c.String(http.StatusOK, connectionString)
1✔
332
}
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