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

vocdoni / saas-backend / 16141698976

08 Jul 2025 11:15AM UTC coverage: 56.122% (-0.008%) from 56.13%
16141698976

Pull #163

github

altergui
all: fix some renamings left behind (member -> participant, etc)
Pull Request #163: fix/leftover renames

17 of 25 new or added lines in 3 files covered. (68.0%)

1 existing line in 1 file now uncovered.

4854 of 8649 relevant lines covered (56.12%)

24.32 hits per line

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

51.05
/csp/handlers/handlers.go
1
// Package handlers provides HTTP handlers for the CSP
2
// API endpoints, managing authentication, token verification, and cryptographic
3
// signing operations for process bundles in the Vocdoni voting platform.
4
package handlers
5

6
import (
7
        "bytes"
8
        "encoding/json"
9
        "fmt"
10
        "net/http"
11
        "strconv"
12
        "strings"
13

14
        "github.com/ethereum/go-ethereum/common"
15
        "github.com/go-chi/chi/v5"
16
        "github.com/vocdoni/saas-backend/api/apicommon"
17
        "github.com/vocdoni/saas-backend/csp"
18
        "github.com/vocdoni/saas-backend/csp/notifications"
19
        "github.com/vocdoni/saas-backend/csp/signers"
20
        "github.com/vocdoni/saas-backend/db"
21
        "github.com/vocdoni/saas-backend/errors"
22
        "github.com/vocdoni/saas-backend/internal"
23
        "go.vocdoni.io/dvote/log"
24
        "go.vocdoni.io/dvote/vochain/state"
25
)
26

27
// CSPHandlers is a struct that contains an instance of the CSP and the main
28
// database (where the bundle and census data is stored). It is used to handle
29
// the CSP API requests such as the authentication and signing of the bundle
30
// processes.
31
type CSPHandlers struct {
32
        csp    *csp.CSP
33
        mainDB *db.MongoStorage
34
}
35

36
// New creates a new instance of the CSP handlers instance. It receives the CSP
37
// instance and the main database instance as parameters.
38
func New(c *csp.CSP, mainDB *db.MongoStorage) *CSPHandlers {
1✔
39
        return &CSPHandlers{
1✔
40
                csp:    c,
1✔
41
                mainDB: mainDB,
1✔
42
        }
1✔
43
}
1✔
44

45
// parseBundleID parses the bundle ID from the URL parameters
46
func parseBundleID(w http.ResponseWriter, r *http.Request) (*internal.HexBytes, bool) {
18✔
47
        bundleID := new(internal.HexBytes)
18✔
48
        if err := bundleID.ParseString(chi.URLParam(r, "bundleId")); err != nil {
18✔
49
                errors.ErrMalformedURLParam.Withf("invalid bundle ID").Write(w)
×
50
                return nil, false
×
51
        }
×
52
        return bundleID, true
18✔
53
}
54

55
// parseAuthStep parses the authentication step from the URL parameters
56
func parseAuthStep(w http.ResponseWriter, r *http.Request) (int, bool) {
13✔
57
        stepString := chi.URLParam(r, "step")
13✔
58
        step, err := strconv.Atoi(stepString)
13✔
59
        if err != nil || (step != 0 && step != 1) {
13✔
60
                errors.ErrMalformedURLParam.Withf("wrong step ID").Write(w)
×
61
                return 0, false
×
62
        }
×
63
        return step, true
13✔
64
}
65

66
// getBundle retrieves the bundle from the database
67
func (c *CSPHandlers) getBundle(w http.ResponseWriter, bundleID internal.HexBytes) (*db.ProcessesBundle, bool) {
18✔
68
        bundle, err := c.mainDB.ProcessBundle(bundleID)
18✔
69
        if err != nil {
18✔
70
                if err == db.ErrNotFound {
×
71
                        errors.ErrMalformedURLParam.Withf("bundle not found").Write(w)
×
72
                        return nil, false
×
73
                }
×
74
                errors.ErrGenericInternalServerError.WithErr(err).Write(w)
×
75
                return nil, false
×
76
        }
77

78
        // Check if the bundle has processes
79
        if len(bundle.Processes) == 0 {
18✔
80
                errors.ErrInvalidOrganizationData.Withf("bundle has no processes").Write(w)
×
81
                return nil, false
×
82
        }
×
83

84
        return bundle, true
18✔
85
}
86

87
// handleAuthStep handles the authentication step and writes the response
88
func (c *CSPHandlers) handleAuthStep(w http.ResponseWriter, r *http.Request,
89
        step int, bundleID internal.HexBytes, censusID string,
90
) {
13✔
91
        var authToken internal.HexBytes
13✔
92
        var err error
13✔
93

13✔
94
        if step == 0 {
20✔
95
                authToken, err = c.authFirstStep(r, bundleID, censusID)
7✔
96
        } else {
13✔
97
                authToken, err = c.authSecondStep(r)
6✔
98
        }
6✔
99

100
        if err != nil {
16✔
101
                if apiErr, ok := err.(errors.Error); ok {
6✔
102
                        apiErr.Write(w)
3✔
103
                        return
3✔
104
                }
3✔
105
                errors.ErrUnauthorized.WithErr(err).Write(w)
×
106
                return
×
107
        }
108

109
        apicommon.HTTPWriteJSON(w, &AuthResponse{AuthToken: authToken})
10✔
110
}
111

112
// BundleAuthHandler godoc
113
//
114
//        @Summary                Authenticate for a process bundle
115
//        @Description        Handle authentication for a process bundle. There are two steps in the authentication process:
116
//        @Description        - Step 0: The user sends the participant ID and contact information (email or phone).
117
//        @Description        If valid, the server sends a challenge to the user with a token.
118
//        @Description        - Step 1: The user sends the token and challenge solution back to the server.
119
//        @Description        If valid, the token is marked as verified and returned.
120
//        @Tags                        process
121
//        @Accept                        json
122
//        @Produce                json
123
//        @Param                        bundleId        path                string                true        "Bundle ID"
124
//        @Param                        step                path                string                true        "Authentication step (0 or 1)"
125
//        @Param                        request                body                interface{}        true        "Authentication request (varies by step)"
126
//        @Success                200                        {object}        handlers.AuthResponse
127
//        @Failure                400                        {object}        errors.Error        "Invalid input data"
128
//        @Failure                401                        {object}        errors.Error        "Unauthorized"
129
//        @Failure                404                        {object}        errors.Error        "Bundle not found"
130
//        @Failure                500                        {object}        errors.Error        "Internal server error"
131
//        @Router                        /process/bundle/{bundleId}/auth/{step} [post]
132
func (c *CSPHandlers) BundleAuthHandler(w http.ResponseWriter, r *http.Request) {
13✔
133
        // Parse the bundle ID and authentication step
13✔
134
        bundleID, ok := parseBundleID(w, r)
13✔
135
        if !ok {
13✔
136
                return
×
137
        }
×
138

139
        step, ok := parseAuthStep(w, r)
13✔
140
        if !ok {
13✔
141
                return
×
142
        }
×
143

144
        // Get the bundle
145
        bundle, ok := c.getBundle(w, *bundleID)
13✔
146
        if !ok {
13✔
147
                return
×
148
        }
×
149

150
        // Handle the authentication step
151
        c.handleAuthStep(w, r, step, *bundleID, bundle.Census.ID.Hex())
13✔
152
}
153

154
// parseSignRequest parses the sign request from the request body
155
func parseSignRequest(w http.ResponseWriter, r *http.Request) (*SignRequest, bool) {
5✔
156
        var req SignRequest
5✔
157
        if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
5✔
158
                errors.ErrMalformedBody.Write(w)
×
159
                return nil, false
×
160
        }
×
161

162
        // Check that the request contains the auth token
163
        if req.AuthToken == nil {
5✔
164
                errors.ErrUnauthorized.Withf("missing auth token").Write(w)
×
165
                return nil, false
×
166
        }
×
167

168
        return &req, true
5✔
169
}
170

171
// getAuthInfo retrieves the authentication information from the token
172
func (c *CSPHandlers) getAuthInfo(w http.ResponseWriter, authToken internal.HexBytes) (*db.CSPAuth, bool) {
5✔
173
        auth, err := c.csp.Storage.CSPAuth(authToken)
5✔
174
        if err != nil {
5✔
175
                errors.ErrUnauthorized.WithErr(err).Write(w)
×
176
                return nil, false
×
177
        }
×
178
        return auth, true
5✔
179
}
180

181
// findProcessInBundle checks if the process is part of the bundle
182
func findProcessInBundle(bundle *db.ProcessesBundle, processID internal.HexBytes) (internal.HexBytes, bool) {
5✔
183
        for _, pID := range bundle.Processes {
10✔
184
                if bytes.Equal(pID, processID) {
10✔
185
                        return pID, true
5✔
186
                }
5✔
187
        }
188
        return nil, false
×
189
}
190

191
// checkCensusParticipant checks if the participant is in the census
192
func (c *CSPHandlers) checkCensusParticipant(w http.ResponseWriter, censusID string, userID string) bool {
5✔
193
        // Get census information
5✔
194
        census, err := c.mainDB.Census(censusID)
5✔
195
        if err != nil {
5✔
196
                if err == db.ErrNotFound {
×
197
                        return false
×
198
                }
×
199
                return false
×
200
        }
201
        if _, err := c.mainDB.CensusParticipantByMemberNumber(censusID, userID, census.OrgAddress); err != nil {
5✔
202
                if err == db.ErrNotFound {
×
203
                        errors.ErrUnauthorized.Withf("participant not found in the census").Write(w)
×
204
                        return false
×
205
                }
×
206
                log.Warnw("error getting census participant", "error", err)
×
207
                errors.ErrGenericInternalServerError.WithErr(err).Write(w)
×
208
                return false
×
209
        }
210
        return true
5✔
211
}
212

213
// parseAddress parses the address from the payload
214
func parseAddress(w http.ResponseWriter, payload string) (*internal.HexBytes, bool) {
5✔
215
        address := new(internal.HexBytes)
5✔
216
        if err := address.ParseString(payload); err != nil {
5✔
217
                errors.ErrMalformedBody.WithErr(err).Write(w)
×
218
                return nil, false
×
219
        }
×
220
        return address, true
5✔
221
}
222

223
// signAndRespond signs the request and sends the response
224
func (c *CSPHandlers) signAndRespond(w http.ResponseWriter, authToken, address, processID internal.HexBytes) {
5✔
225
        log.Debugw("new CSP sign request", "address", address, "procId", processID)
5✔
226

5✔
227
        signature, err := c.csp.Sign(authToken, address, processID, signers.SignerTypeECDSASalted)
5✔
228
        if err != nil {
6✔
229
                errors.ErrUnauthorized.WithErr(err).Write(w)
1✔
230
                return
1✔
231
        }
1✔
232

233
        apicommon.HTTPWriteJSON(w, &AuthResponse{Signature: signature})
4✔
234
}
235

236
// BundleSignHandler godoc
237
//
238
//        @Summary                Sign a process in a bundle
239
//        @Description        Sign a process in a bundle. Requires a verified token. The server signs the address with the user data
240
//        @Description        and returns the signature. Once signed, the process is marked as consumed and cannot be signed again.
241
//        @Tags                        process
242
//        @Accept                        json
243
//        @Produce                json
244
//        @Param                        bundleId        path                string                                        true        "Bundle ID"
245
//        @Param                        request                body                handlers.SignRequest        true        "Sign request with process ID, auth token, and payload"
246
//        @Success                200                        {object}        handlers.AuthResponse
247
//        @Failure                400                        {object}        errors.Error        "Invalid input data"
248
//        @Failure                401                        {object}        errors.Error        "Unauthorized or invalid token"
249
//        @Failure                404                        {object}        errors.Error        "Bundle not found"
250
//        @Failure                500                        {object}        errors.Error        "Internal server error"
251
//        @Router                        /process/bundle/{bundleId}/sign [post]
252
func (c *CSPHandlers) BundleSignHandler(w http.ResponseWriter, r *http.Request) {
5✔
253
        // Parse the bundle ID
5✔
254
        bundleID, ok := parseBundleID(w, r)
5✔
255
        if !ok {
5✔
256
                return
×
257
        }
×
258

259
        // Get the bundle
260
        bundle, ok := c.getBundle(w, *bundleID)
5✔
261
        if !ok {
5✔
262
                return
×
263
        }
×
264

265
        // Parse the sign request
266
        req, ok := parseSignRequest(w, r)
5✔
267
        if !ok {
5✔
268
                return
×
269
        }
×
270

271
        // Get the authentication information
272
        auth, ok := c.getAuthInfo(w, req.AuthToken)
5✔
273
        if !ok {
5✔
274
                return
×
275
        }
×
276

277
        // Find the process in the bundle
278
        processID, found := findProcessInBundle(bundle, req.ProcessID)
5✔
279
        if !found {
5✔
280
                errors.ErrUnauthorized.Withf("process not found in bundle").Write(w)
×
281
                return
×
282
        }
×
283

284
        // Check if the participant is in the census
285
        if !c.checkCensusParticipant(w, bundle.Census.ID.Hex(), string(auth.UserID)) {
5✔
286
                return
×
287
        }
×
288

289
        // Parse the address from the payload
290
        address, ok := parseAddress(w, req.Payload)
5✔
291
        if !ok {
5✔
292
                return
×
293
        }
×
294

295
        // Sign the request and send the response
296
        c.signAndRespond(w, req.AuthToken, *address, processID)
5✔
297
}
298

299
// ConsumedAddressHandler godoc
300
//
301
//        @Summary                Get the address used to sign a process
302
//        @Description        Get the address used to sign a process. Requires a verified token. Returns the address, nullifier,
303
//        @Description        and timestamp of the consumption.
304
//        @Tags                        process
305
//        @Accept                        json
306
//        @Produce                json
307
//        @Param                        processId        path                string                                                        true        "Process ID"
308
//        @Param                        request                body                handlers.ConsumedAddressRequest        true        "Request with auth token"
309
//        @Success                200                        {object}        handlers.ConsumedAddressResponse
310
//        @Failure                400                        {object}        errors.Error        "Invalid input data"
311
//        @Failure                401                        {object}        errors.Error        "Unauthorized or invalid token"
312
//        @Failure                404                        {object}        errors.Error        "Process not found"
313
//        @Failure                500                        {object}        errors.Error        "Internal server error"
314
//        @Router                        /process/{processId}/sign-info [post]
315
func (c *CSPHandlers) ConsumedAddressHandler(w http.ResponseWriter, r *http.Request) {
×
316
        // get the bundle ID from the URL parameters
×
317
        processID := new(internal.HexBytes)
×
318
        if err := processID.ParseString(chi.URLParam(r, "processId")); err != nil {
×
319
                errors.ErrMalformedURLParam.WithErr(csp.ErrNoBundleID).Write(w)
×
320
                return
×
321
        }
×
322
        // parse the request from the body
323
        var req ConsumedAddressRequest
×
324
        if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
×
325
                errors.ErrMalformedBody.Write(w)
×
326
                return
×
327
        }
×
328
        // get the user data from the token
329
        auth, err := c.csp.Storage.CSPAuth(req.AuthToken)
×
330
        if err != nil {
×
331
                log.Warnw("error getting user data by token",
×
332
                        "error", err,
×
333
                        "token", req.AuthToken)
×
334
                errors.ErrUnauthorized.WithErr(err).Write(w)
×
335
                return
×
336
        }
×
337
        // check if the token is verified
338
        if !auth.Verified {
×
339
                errors.ErrUnauthorized.WithErr(csp.ErrAuthTokenNotVerified).Write(w)
×
340
                return
×
341
        }
×
342
        cspProcess, err := c.csp.Storage.CSPProcess(auth.Token, *processID)
×
343
        if err != nil {
×
344
                log.Warnw("error getting user data by token",
×
345
                        "error", err,
×
346
                        "token", req.AuthToken)
×
347
                errors.ErrUnauthorized.WithErr(err).Write(w)
×
348
                return
×
349
        }
×
350
        // check if the process has been consumed and return error if not
351
        if !cspProcess.Consumed {
×
352
                errors.ErrUserNoVoted.Write(w)
×
353
                return
×
354
        }
×
355
        // return the address used to sign the process and the nullifier
356
        apicommon.HTTPWriteJSON(w, &ConsumedAddressResponse{
×
357
                Address:   cspProcess.ConsumedAddress,
×
358
                Nullifier: state.GenerateNullifier(common.BytesToAddress(cspProcess.ConsumedAddress), *processID),
×
359
                At:        cspProcess.ConsumedAt,
×
360
        })
×
361
}
362

363
// validateParticipantID checks if the participant ID is provided
364
func validateParticipantID(participantID string) error {
7✔
365
        if len(participantID) == 0 {
7✔
NEW
366
                return errors.ErrInvalidUserData.Withf("participant ID not provided")
×
UNCOV
367
        }
×
368
        return nil
7✔
369
}
370

371
// validateContactInfo checks if at least one contact method is provided
372
func validateContactInfo(email, phone string) error {
7✔
373
        if len(email) == 0 && len(phone) == 0 {
7✔
374
                return errors.ErrInvalidUserData.Withf("no contact information provided (email or phone)")
×
375
        }
×
376
        return nil
7✔
377
}
378

379
// validateEmail validates the email format if provided
380
func validateEmail(email string) error {
7✔
381
        if len(email) > 0 && !internal.ValidEmail(email) {
7✔
382
                return errors.ErrInvalidUserData.Withf("invalid email format")
×
383
        }
×
384
        return nil
7✔
385
}
386

387
// validatePhone validates and sanitizes the phone number if provided
388
func validatePhone(phone *string) error {
7✔
389
        if len(*phone) == 0 {
14✔
390
                return nil
7✔
391
        }
7✔
392

393
        sanitizedPhone, err := internal.SanitizeAndVerifyPhoneNumber(*phone)
×
394
        if err != nil {
×
395
                log.Warnw("invalid phone number format", "phone", *phone, "error", err)
×
396
                return errors.ErrInvalidUserData.Withf("invalid phone number format")
×
397
        }
×
398
        *phone = sanitizedPhone
×
399
        return nil
×
400
}
401

402
// validateAuthRequest validates the authentication request data
403
func validateAuthRequest(req *AuthRequest) error {
7✔
404
        // Check request participant ID
7✔
405
        err := validateParticipantID(req.ParticipantID)
7✔
406
        if err != nil {
7✔
407
                return err
×
408
        }
×
409

410
        // Check request contact information
411
        err = validateContactInfo(req.Email, req.Phone)
7✔
412
        if err != nil {
7✔
413
                return err
×
414
        }
×
415

416
        // Validate email if provided
417
        err = validateEmail(req.Email)
7✔
418
        if err != nil {
7✔
419
                return err
×
420
        }
×
421

422
        // Validate phone if provided
423
        return validatePhone(&req.Phone)
7✔
424
}
425

426
// getCensusAndOrgMember retrieves the census and org member information
427
func (c *CSPHandlers) getCensusAndOrgMember(censusID string, participantID string) (*db.Census, *db.OrgMember, error) {
7✔
428
        // Get census information
7✔
429
        census, err := c.mainDB.Census(censusID)
7✔
430
        if err != nil {
7✔
431
                if err == db.ErrNotFound {
×
432
                        return nil, nil, errors.ErrCensusNotFound
×
433
                }
×
434
                return nil, nil, errors.ErrGenericInternalServerError.WithErr(err)
×
435
        }
436

437
        // Check the participant is in the census
438
        if _, err := c.mainDB.CensusParticipantByMemberNumber(censusID, participantID, census.OrgAddress); err != nil {
8✔
439
                if err == db.ErrNotFound {
2✔
440
                        return nil, nil, errors.ErrUnauthorized.Withf("participant not found in the census")
1✔
441
                }
1✔
442
                return nil, nil, errors.ErrGenericInternalServerError.WithErr(err)
×
443
        }
444

445
        // Fetch the correspoding org member
446
        orgMember, err := c.mainDB.OrgMemberByMemberNumber(census.OrgAddress, participantID)
6✔
447
        if err != nil {
6✔
NEW
448
                return nil, nil, fmt.Errorf("failed to get org member: %w", err)
×
NEW
449
        }
×
450

451
        return census, orgMember, nil
6✔
452
}
453

454
// verifyPassword checks if the provided password matches the member's hashed password
455
func (c *CSPHandlers) verifyPassword(password string, hashedPass []byte) error {
6✔
456
        if password != "" && !bytes.Equal(internal.HashPassword(c.csp.PasswordSalt, password), hashedPass) {
6✔
457
                return fmt.Errorf("invalid user data")
×
458
        }
×
459
        return nil
6✔
460
}
461

462
// verifyEmail checks if the provided email matches the member's stored email
463
func verifyEmail(email string, storedEmail string) error {
6✔
464
        if !strings.EqualFold(email, storedEmail) {
7✔
465
                return errors.ErrUnauthorized.Withf("invalid user email")
1✔
466
        }
1✔
467
        return nil
5✔
468
}
469

470
// verifyPhone checks if the provided phone matches the member's hashed phone
471
func verifyPhone(orgAddress string, phone string, hashedPhone []byte) error {
×
472
        if !bytes.Equal(internal.HashOrgData(orgAddress, phone), hashedPhone) {
×
473
                return errors.ErrUnauthorized.Withf("invalid user phone")
×
474
        }
×
475
        return nil
×
476
}
477

478
// handleEmailContact verifies the email and returns the appropriate contact method
479
func handleEmailContact(
480
        email string,
481
        storedEmail string,
482
) (string, notifications.ChallengeType, error) {
6✔
483
        if err := verifyEmail(email, storedEmail); err != nil {
7✔
484
                return "", "", err
1✔
485
        }
1✔
486
        return email, notifications.EmailChallenge, nil
5✔
487
}
488

489
// handlePhoneContact verifies the phone and returns the appropriate contact method
490
func handlePhoneContact(
491
        orgAddress string,
492
        phone string,
493
        hashedPhone []byte,
494
) (string, notifications.ChallengeType, error) {
×
495
        if err := verifyPhone(orgAddress, phone, hashedPhone); err != nil {
×
496
                return "", "", err
×
497
        }
×
498
        return phone, notifications.SMSChallenge, nil
×
499
}
500

501
// determineContactMethod determines the contact method based on the census type and request data
502
func determineContactMethod(
503
        census *db.Census,
504
        req *AuthRequest,
505
        member *db.OrgMember,
506
) (string, notifications.ChallengeType, error) {
6✔
507
        switch census.Type {
6✔
508
        case db.CensusTypeMail:
×
509
                return handleEmailContact(req.Email, member.Email)
×
510

511
        case db.CensusTypeSMS:
×
512
                return handlePhoneContact(census.OrgAddress, req.Phone, member.HashedPhone)
×
513

514
        case db.CensusTypeSMSorMail:
6✔
515
                if req.Email != "" {
12✔
516
                        return handleEmailContact(req.Email, member.Email)
6✔
517
                }
6✔
518

519
                if req.Phone != "" {
×
520
                        return handlePhoneContact(census.OrgAddress, req.Phone, member.HashedPhone)
×
521
                }
×
522
        }
523

524
        return "", "", errors.ErrNotSupported.Withf("invalid census type")
×
525
}
526

527
// authFirstStep is the first step of the authentication process. It receives
528
// the request, the bundle ID and the census ID as parameters. It checks the
529
// request data (participant ID, email and phone) against the census data.
530
// If the data is valid, it generates a token with the bundle ID, the
531
// participant ID as the user ID, the contact information as the
532
// destination and the challenge type. It returns the token and an error if
533
// any. It sends the challenge to the user (email or SMS) to verify the user
534
// token in the second step.
535
func (c *CSPHandlers) authFirstStep(
536
        r *http.Request,
537
        bundleID internal.HexBytes,
538
        censusID string,
539
) (internal.HexBytes, error) {
7✔
540
        // Parse and validate request
7✔
541
        var req AuthRequest
7✔
542
        if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
7✔
543
                return nil, errors.ErrMalformedBody.Withf("invalid JSON request")
×
544
        }
×
545

546
        if err := validateAuthRequest(&req); err != nil {
7✔
547
                return nil, err
×
548
        }
×
549

550
        // Get census and participant information
551
        census, orgMember, err := c.getCensusAndOrgMember(censusID, req.ParticipantID)
7✔
552
        if err != nil {
8✔
553
                return nil, err
1✔
554
        }
1✔
555

556
        // Verify password if provided
557
        if err := c.verifyPassword(req.Password, orgMember.HashedPass); err != nil {
6✔
558
                return nil, err
×
559
        }
×
560

561
        // Determine contact method based on census type
562
        toDestinations, challengeType, err := determineContactMethod(census, &req, orgMember)
6✔
563
        if err != nil {
7✔
564
                return nil, err
1✔
565
        }
1✔
566

567
        // Generate the token
568
        return c.csp.BundleAuthToken(bundleID, internal.HexBytes(orgMember.MemberNumber), toDestinations, challengeType)
5✔
569
}
570

571
// authSecondStep is the second step of the authentication process. It
572
// receives the request and checks the token and the challenge solution
573
// against the server data. If the data is valid, it returns the token and
574
// an error if any. It the solution is valid, the token is marked as verified
575
// and returned to the user. The user can use the token to sign the bundle
576
// processes.
577
func (c *CSPHandlers) authSecondStep(r *http.Request) (internal.HexBytes, error) {
6✔
578
        var req AuthChallengeRequest
6✔
579
        if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
6✔
580
                return nil, errors.ErrMalformedBody.Withf("invalid JSON request")
×
581
        }
×
582
        err := c.csp.VerifyBundleAuthToken(req.AuthToken, req.AuthData[0])
6✔
583
        if err == nil {
11✔
584
                return req.AuthToken, nil
5✔
585
        }
5✔
586

587
        switch err {
1✔
588
        case csp.ErrInvalidAuthToken, csp.ErrInvalidSolution, csp.ErrChallengeCodeFailure:
1✔
589
                return nil, errors.ErrUnauthorized.WithErr(err)
1✔
590
        case csp.ErrUserUnknown, csp.ErrUserNotBelongsToBundle:
×
591
                return nil, errors.ErrUserNotFound.WithErr(err)
×
592
        case csp.ErrStorageFailure:
×
593
                return nil, errors.ErrInternalStorageError.WithErr(err)
×
594
        default:
×
595
                return nil, errors.ErrGenericInternalServerError.WithErr(err)
×
596
        }
597
}
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

© 2025 Coveralls, Inc