You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

recaptcha.go 2.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. // Copyright 2018 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package recaptcha
  4. import (
  5. "context"
  6. "fmt"
  7. "io"
  8. "net/http"
  9. "net/url"
  10. "strings"
  11. "code.gitea.io/gitea/modules/json"
  12. "code.gitea.io/gitea/modules/setting"
  13. "code.gitea.io/gitea/modules/util"
  14. )
  15. // Response is the structure of JSON returned from API
  16. type Response struct {
  17. Success bool `json:"success"`
  18. ChallengeTS string `json:"challenge_ts"`
  19. Hostname string `json:"hostname"`
  20. ErrorCodes []ErrorCode `json:"error-codes"`
  21. }
  22. const apiURL = "api/siteverify"
  23. // Verify calls Google Recaptcha API to verify token
  24. func Verify(ctx context.Context, response string) (bool, error) {
  25. post := url.Values{
  26. "secret": {setting.Service.RecaptchaSecret},
  27. "response": {response},
  28. }
  29. // Basically a copy of http.PostForm, but with a context
  30. req, err := http.NewRequestWithContext(ctx, http.MethodPost,
  31. util.URLJoin(setting.Service.RecaptchaURL, apiURL), strings.NewReader(post.Encode()))
  32. if err != nil {
  33. return false, fmt.Errorf("Failed to create CAPTCHA request: %w", err)
  34. }
  35. req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  36. resp, err := http.DefaultClient.Do(req)
  37. if err != nil {
  38. return false, fmt.Errorf("Failed to send CAPTCHA response: %s", err)
  39. }
  40. defer resp.Body.Close()
  41. body, err := io.ReadAll(resp.Body)
  42. if err != nil {
  43. return false, fmt.Errorf("Failed to read CAPTCHA response: %s", err)
  44. }
  45. var jsonResponse Response
  46. err = json.Unmarshal(body, &jsonResponse)
  47. if err != nil {
  48. return false, fmt.Errorf("Failed to parse CAPTCHA response: %s", err)
  49. }
  50. var respErr error
  51. if len(jsonResponse.ErrorCodes) > 0 {
  52. respErr = jsonResponse.ErrorCodes[0]
  53. }
  54. return jsonResponse.Success, respErr
  55. }
  56. // ErrorCode is a reCaptcha error
  57. type ErrorCode string
  58. // String fulfills the Stringer interface
  59. func (e ErrorCode) String() string {
  60. switch e {
  61. case "missing-input-secret":
  62. return "The secret parameter is missing."
  63. case "invalid-input-secret":
  64. return "The secret parameter is invalid or malformed."
  65. case "missing-input-response":
  66. return "The response parameter is missing."
  67. case "invalid-input-response":
  68. return "The response parameter is invalid or malformed."
  69. case "bad-request":
  70. return "The request is invalid or malformed."
  71. case "timeout-or-duplicate":
  72. return "The response is no longer valid: either is too old or has been used previously."
  73. }
  74. return string(e)
  75. }
  76. // Error fulfills the error interface
  77. func (e ErrorCode) Error() string {
  78. return e.String()
  79. }