diff --git a/controllers/main.go b/controllers/main.go index 78da9b23..b2a9e9f1 100644 --- a/controllers/main.go +++ b/controllers/main.go @@ -287,6 +287,10 @@ func (controller *MainController) API(c web.C, r *http.Request) *system.APIRespo } } + if invalidAuthMessage := c.Env["InvalidTicketAuthMessage"]; invalidAuthMessage != "" && code == codes.Unauthenticated { + err = fmt.Errorf("error processing ticket auth data: %s", invalidAuthMessage) + } + if err != nil { status = "error" response = response + " - " + err.Error() diff --git a/v3api/middleware.go b/v3api/middleware.go index 98a138a1..6d2b593b 100644 --- a/v3api/middleware.go +++ b/v3api/middleware.go @@ -13,8 +13,11 @@ func (v3Api *V3API) ApplyTicketAuth(c *web.C, h http.Handler) http.Handler { fn := func(w http.ResponseWriter, r *http.Request) { if strings.HasPrefix(r.URL.Path, "/api/v3") { authHeader := r.Header.Get("Authorization") - msa := v3Api.validateTicketOwnership(authHeader) - if msa != "" { + msa, invalidAuthResponse := v3Api.validateTicketOwnership(authHeader) + if invalidAuthResponse != "" { + log.Warnf(invalidAuthResponse) + c.Env["InvalidTicketAuthMessage"] = invalidAuthResponse + } else if msa != "" { dbMap := c.Env["DbMap"].(*gorp.DbMap) user, err := models.GetUserByMSA(dbMap, msa) diff --git a/v3api/ticketauth.go b/v3api/ticketauth.go index 8a557730..69b7c47d 100644 --- a/v3api/ticketauth.go +++ b/v3api/ticketauth.go @@ -8,6 +8,7 @@ import ( "github.com/decred/dcrd/dcrutil" "github.com/decred/dcrwallet/wallet" + "fmt" ) const ( @@ -19,41 +20,41 @@ const ( MaxTicketChallengeAge = 60 * 30 // 30 minutes ) -func (v3Api *V3API) validateTicketOwnership(authHeader string) (multiSigAddress string) { +func (v3Api *V3API) validateTicketOwnership(authHeader string) (multiSigAddress, authValidationFailureReason string) { if !strings.HasPrefix(authHeader, customAuthScheme) { - log.Warnf("invalid API v3 auth header value %s", authHeader) + authValidationFailureReason = fmt.Sprintf("invalid API v3 auth header value %s", authHeader) return } timestamp, timestampSignature, ticketHash := extractAuthParams(strings.TrimPrefix(authHeader, customAuthScheme)) if timestamp == "" || timestampSignature == "" || ticketHash == "" { - log.Warnf("invalid API v3 auth header value %s", authHeader) + authValidationFailureReason = fmt.Sprintf("invalid API v3 auth header value %s", authHeader) return } // Ensure that this signature had not been used in a previous authentication attempt. if v3Api.processedTicketChallenges.containsChallenge(timestampSignature) { - log.Warnf("disallowed reuse of ticket auth signature %v", timestampSignature) + authValidationFailureReason = fmt.Sprintf("disallowed reuse of ticket auth signature %v", timestampSignature) return } authTimestamp, err := strconv.Atoi(timestamp) if err != nil { - log.Warnf("invalid ticket auth timestamp value %v", timestamp) + authValidationFailureReason = fmt.Sprintf("invalid ticket auth timestamp value %v", timestamp) return } // Ensure that the auth timestamp is not in the future and is not more than 30 seconds into the past. timestampDelta := time.Now().Unix() - int64(authTimestamp) if timestampDelta < 0 || timestampDelta > v3Api.ticketChallengeMaxAge { - log.Warnf("expired ticket auth timestamp value %v", timestamp) + authValidationFailureReason = fmt.Sprintf("expired ticket auth timestamp value %v", timestamp) return } // confirm that the timestamp signature is a valid base64 string decodedSignature, err := base64.StdEncoding.DecodeString(timestampSignature) if err != nil { - log.Warnf("invalid ticket auth signature %s", timestampSignature) + authValidationFailureReason = fmt.Sprintf("invalid ticket auth signature %s", timestampSignature) return } @@ -67,20 +68,20 @@ func (v3Api *V3API) validateTicketOwnership(authHeader string) (multiSigAddress // todo: may be better to maintain a memory map of tickets-userWalletAddresses ticketInfo, err := v3Api.stakepooldConnMan.GetTicketInfo(ticketHash) if err != nil { - log.Warnf("ticket auth, get ticket info failed: %v", err) + authValidationFailureReason = fmt.Sprintf("ticket auth, get ticket info failed: %v", err) return } // check if timestamp signature checks out against address addr, err := dcrutil.DecodeAddress(ticketInfo.OwnerFeeAddress) if err != nil { - log.Errorf("ticket auth, unexpected decode address error: %v", err) + authValidationFailureReason = fmt.Sprintf("ticket auth, unexpected decode address error: %v", err) return } valid, err := wallet.VerifyMessage(timestamp, addr, decodedSignature) if err != nil { - log.Errorf("error validating timestamp signature for ticket auth %v", err) + authValidationFailureReason = fmt.Sprintf("error validating timestamp signature for ticket auth %v", err) return }