chatdesk-ui/auth_v2.169.0/internal/security/captcha.go

102 lines
2.8 KiB
Go

package security
import (
"encoding/json"
"log"
"net/http"
"net/url"
"os"
"strconv"
"strings"
"time"
"fmt"
"github.com/pkg/errors"
"github.com/supabase/auth/internal/utilities"
)
type GotrueRequest struct {
Security GotrueSecurity `json:"gotrue_meta_security"`
}
type GotrueSecurity struct {
Token string `json:"captcha_token"`
}
type VerificationResponse struct {
Success bool `json:"success"`
ErrorCodes []string `json:"error-codes"`
Hostname string `json:"hostname"`
}
var Client *http.Client
func init() {
var defaultTimeout time.Duration = time.Second * 10
timeoutStr := os.Getenv("GOTRUE_SECURITY_CAPTCHA_TIMEOUT")
if timeoutStr != "" {
if timeout, err := time.ParseDuration(timeoutStr); err != nil {
log.Fatalf("error loading GOTRUE_SECURITY_CAPTCHA_TIMEOUT: %v", err.Error())
} else if timeout != 0 {
defaultTimeout = timeout
}
}
Client = &http.Client{Timeout: defaultTimeout}
}
func VerifyRequest(requestBody *GotrueRequest, clientIP, secretKey, captchaProvider string) (VerificationResponse, error) {
captchaResponse := strings.TrimSpace(requestBody.Security.Token)
if captchaResponse == "" {
return VerificationResponse{}, errors.New("no captcha response (captcha_token) found in request")
}
captchaURL, err := GetCaptchaURL(captchaProvider)
if err != nil {
return VerificationResponse{}, err
}
return verifyCaptchaCode(captchaResponse, secretKey, clientIP, captchaURL)
}
func verifyCaptchaCode(token, secretKey, clientIP, captchaURL string) (VerificationResponse, error) {
data := url.Values{}
data.Set("secret", secretKey)
data.Set("response", token)
data.Set("remoteip", clientIP)
// TODO (darora): pipe through sitekey
r, err := http.NewRequest("POST", captchaURL, strings.NewReader(data.Encode()))
if err != nil {
return VerificationResponse{}, errors.Wrap(err, "couldn't initialize request object for captcha check")
}
r.Header.Add("Content-Type", "application/x-www-form-urlencoded")
r.Header.Add("Content-Length", strconv.Itoa(len(data.Encode())))
res, err := Client.Do(r)
if err != nil {
return VerificationResponse{}, errors.Wrap(err, "failed to verify captcha response")
}
defer utilities.SafeClose(res.Body)
var verificationResponse VerificationResponse
if err := json.NewDecoder(res.Body).Decode(&verificationResponse); err != nil {
return VerificationResponse{}, errors.Wrap(err, "failed to decode captcha response: not JSON")
}
return verificationResponse, nil
}
func GetCaptchaURL(captchaProvider string) (string, error) {
switch captchaProvider {
case "hcaptcha":
return "https://hcaptcha.com/siteverify", nil
case "turnstile":
return "https://challenges.cloudflare.com/turnstile/v0/siteverify", nil
default:
return "", fmt.Errorf("captcha Provider %q could not be found", captchaProvider)
}
}