@@ -38,11 +38,15 @@ type HcaptchaResponse CaptchaResponse
38
38
// ReCaptchaResponse is the CaptchaResponse for Google ReCaptcha
39
39
type ReCaptchaResponse CaptchaResponse
40
40
41
+ // TurnstileResponse is the CaptchaResponse for Cloudflare Turnstile
42
+ type TurnstileResponse CaptchaResponse
43
+
41
44
// List of common errors
42
45
const (
43
46
ErrNoValidObject = "no valid form object found"
44
47
ErrHCaptchaValidateFailed = "hCaptcha validation failed"
45
48
ErrReCaptchaVaildateFailed = "reCaptcha validation failed"
49
+ ErrTurnstileVaildateFailed = "Turnstile validation failed"
46
50
)
47
51
48
52
// SendFormBindForm is a middleware that validates the provided form data and binds
@@ -301,6 +305,59 @@ func (r *Route) SendFormRecaptcha(next echo.HandlerFunc) echo.HandlerFunc {
301
305
}
302
306
}
303
307
308
+ // SendFormTurnstile is a middleware that checks the form data against Cloudflare Turnstile
309
+ func (r * Route ) SendFormTurnstile (next echo.HandlerFunc ) echo.HandlerFunc {
310
+ return func (c echo.Context ) error {
311
+ sr := c .Get ("formobj" ).(* SendFormRequest )
312
+ if sr == nil {
313
+ return echo .NewHTTPError (http .StatusInternalServerError , ErrNoValidObject )
314
+ }
315
+
316
+ if sr .FormObj .Validation .Turnstile .Enabled {
317
+ turnstileResponse := c .FormValue ("cf-turnstile-response" )
318
+ if turnstileResponse == "" {
319
+ return echo .NewHTTPError (http .StatusBadRequest , "missing Turnstile response" )
320
+ }
321
+
322
+ // Create a HTTP request
323
+ postData := url.Values {
324
+ "response" : {turnstileResponse },
325
+ "secret" : {sr .FormObj .Validation .Turnstile .SecretKey },
326
+ "remoteip" : {c .RealIP ()},
327
+ }
328
+ httpResp , err := http .PostForm ("https://challenges.cloudflare.com/turnstile/v0/siteverify" , postData )
329
+ if err != nil {
330
+ c .Logger ().Errorf ("failed to post HTTP request to Turnstile: %s" , err )
331
+ return echo .NewHTTPError (http .StatusInternalServerError , ErrTurnstileVaildateFailed )
332
+ }
333
+
334
+ var respBody bytes.Buffer
335
+ _ , err = respBody .ReadFrom (httpResp .Body )
336
+ if err != nil {
337
+ c .Logger ().Errorf ("reading HTTP response body failed: %s" , err )
338
+ return echo .NewHTTPError (http .StatusInternalServerError , ErrTurnstileVaildateFailed )
339
+ }
340
+ if httpResp .StatusCode == http .StatusOK {
341
+ var turnstileResp ReCaptchaResponse
342
+ if err := json .Unmarshal (respBody .Bytes (), & turnstileResp ); err != nil {
343
+ c .Logger ().Errorf ("HTTP response JSON unmarshalling failed: %s" , err )
344
+ return echo .NewHTTPError (http .StatusInternalServerError , ErrTurnstileVaildateFailed )
345
+ }
346
+ if ! turnstileResp .Success {
347
+ return echo .NewHTTPError (http .StatusBadRequest ,
348
+ "Turnstile challenge-response validation failed" )
349
+ }
350
+ return next (c )
351
+ }
352
+
353
+ return echo .NewHTTPError (http .StatusBadRequest ,
354
+ "Turnstile challenge-response validation failed" )
355
+ }
356
+
357
+ return next (c )
358
+ }
359
+ }
360
+
304
361
// SendFormCheckToken is a middleware that checks the form security token
305
362
func (r * Route ) SendFormCheckToken (next echo.HandlerFunc ) echo.HandlerFunc {
306
363
return func (c echo.Context ) error {
0 commit comments