-
-
Notifications
You must be signed in to change notification settings - Fork 5.8k
Add new captcha: cloudflare turnstile #22369
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
a6bfdc5
8c63433
59186ab
0821c09
034dc52
c5d9c49
4ba29b3
09b7707
a152ec7
09ff015
0d185a7
4b3b828
f178ef4
46aa739
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
// Copyright 2023 The Gitea Authors. All rights reserved. | ||
// SPDX-License-Identifier: MIT | ||
|
||
package turnstile | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"io" | ||
"net/http" | ||
"net/url" | ||
"strings" | ||
|
||
"code.gitea.io/gitea/modules/json" | ||
"code.gitea.io/gitea/modules/setting" | ||
) | ||
|
||
// Response is the structure of JSON returned from API | ||
type Response struct { | ||
Success bool `json:"success"` | ||
ChallengeTS string `json:"challenge_ts"` | ||
Hostname string `json:"hostname"` | ||
ErrorCodes []ErrorCode `json:"error-codes"` | ||
Action string `json:"login"` | ||
Cdata string `json:"cdata"` | ||
} | ||
|
||
// Verify calls Cloudflare Turnstile API to verify token | ||
func Verify(ctx context.Context, response, ip string) (bool, error) { | ||
// Cloudflare turnstile official access instruction address: https://developers.cloudflare.com/turnstile/get-started/server-side-validation/ | ||
post := url.Values{ | ||
"secret": {setting.Service.CfTurnstileSecret}, | ||
"response": {response}, | ||
"remoteip": {ip}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Checked with hcaptcha code, hcaptcha also supports There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My personal opinion is the same as this answer. Therefore, I think it may be a better choice to carry
But the usage of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Personally I prefer to keep the same behavior as hcaptcha (no It might still be fine to have the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, I'm going to remove There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It was removed in this commit 46aa739 |
||
} | ||
// Basically a copy of http.PostForm, but with a context | ||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, | ||
"https://challenges.cloudflare.com/turnstile/v0/siteverify", strings.NewReader(post.Encode())) | ||
if err != nil { | ||
return false, fmt.Errorf("Failed to create CAPTCHA request: %w", err) | ||
} | ||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded") | ||
|
||
resp, err := http.DefaultClient.Do(req) | ||
if err != nil { | ||
return false, fmt.Errorf("Failed to send CAPTCHA response: %s", err) | ||
wolfogre marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
defer resp.Body.Close() | ||
body, err := io.ReadAll(resp.Body) | ||
if err != nil { | ||
return false, fmt.Errorf("Failed to read CAPTCHA response: %s", err) | ||
wolfogre marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
var jsonResponse Response | ||
err = json.Unmarshal(body, &jsonResponse) | ||
if err != nil { | ||
wolfogre marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return false, fmt.Errorf("Failed to parse CAPTCHA response: %s", err) | ||
wolfogre marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
var respErr error | ||
if len(jsonResponse.ErrorCodes) > 0 { | ||
respErr = jsonResponse.ErrorCodes[0] | ||
} | ||
return jsonResponse.Success, respErr | ||
} | ||
|
||
// ErrorCode is a reCaptcha error | ||
type ErrorCode string | ||
|
||
// String fulfills the Stringer interface | ||
func (e ErrorCode) String() string { | ||
switch e { | ||
case "missing-input-secret": | ||
return "The secret parameter was not passed." | ||
case "invalid-input-secret": | ||
return "The secret parameter was invalid or did not exist." | ||
case "missing-input-response": | ||
return "The response parameter was not passed." | ||
case "invalid-input-response": | ||
return "The response parameter is invalid or has expired." | ||
case "bad-request": | ||
return "The request was rejected because it was malformed." | ||
case "timeout-or-duplicate": | ||
return "The response parameter has already been validated before." | ||
case "internal-error": | ||
return "An internal error happened while validating the response. The request can be retried." | ||
} | ||
return string(e) | ||
} | ||
|
||
// Error fulfills the error interface | ||
func (e ErrorCode) Error() string { | ||
return e.String() | ||
} |
Uh oh!
There was an error while loading. Please reload this page.