fix(backend): allow WHMCS API login when captcha is enabled

This commit is contained in:
sagitchu
2026-02-23 14:40:29 +08:00
parent 98c463c62b
commit bafcfbde3a
2 changed files with 26 additions and 11 deletions
+14 -11
View File
@@ -219,7 +219,7 @@ func (h *Handler) login(w http.ResponseWriter, r *http.Request) {
response.WriteJSON(w, response.Err(-2, err.Error())) response.WriteJSON(w, response.Err(-2, err.Error()))
return return
} }
if captchaEnabled { if captchaEnabled && !h.apiClientCaptchaBypassEnabled(r) {
captchaID := strings.TrimSpace(req.CaptchaID) captchaID := strings.TrimSpace(req.CaptchaID)
if captchaID == "" { if captchaID == "" {
response.WriteJSON(w, response.ErrDefault("验证码校验失败")) response.WriteJSON(w, response.ErrDefault("验证码校验失败"))
@@ -988,21 +988,24 @@ func (h *Handler) captchaEnabled() (bool, error) {
if err != nil { if err != nil {
return false, err return false, err
} }
if cfg == nil || !strings.EqualFold(cfg.Value, "true") { if cfg == nil {
return false, nil return false, nil
} }
return strings.EqualFold(cfg.Value, "true"), nil
}
// captcha_enabled=true, but we need to verify cloudflare_secret_key is configured func (h *Handler) apiClientCaptchaBypassEnabled(r *http.Request) bool {
// If secret key is not configured, treat captcha as disabled to avoid blocking login if r == nil {
secretKey, err := h.repo.GetConfigByName("cloudflare_secret_key") return false
if err != nil {
return false, err
}
if secretKey == nil || strings.TrimSpace(secretKey.Value) == "" {
return false, nil
} }
return true, nil client := strings.ToLower(strings.TrimSpace(r.Header.Get("X-FLVX-API-Client")))
switch client {
case "whmcs", "whmcs-module":
return true
default:
return false
}
} }
func (h *Handler) markCaptchaToken(token string) { func (h *Handler) markCaptchaToken(token string) {
@@ -45,6 +45,18 @@ func TestCaptchaVerifyLoginContract(t *testing.T) {
assertCodeMsg(t, resp, -1, "验证码校验失败") assertCodeMsg(t, resp, -1, "验证码校验失败")
}) })
t.Run("whmcs api client bypasses captcha", func(t *testing.T) {
body := bytes.NewBufferString(`{"username":"admin_user","password":"admin_user","captchaId":""}`)
req := httptest.NewRequest(http.MethodPost, "/api/v1/user/login", body)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-FLVX-API-Client", "whmcs")
resp := httptest.NewRecorder()
router.ServeHTTP(resp, req)
assertCode(t, resp, 0)
})
t.Run("captcha token is one-time and consumed by login", func(t *testing.T) { t.Run("captcha token is one-time and consumed by login", func(t *testing.T) {
verifyReq := httptest.NewRequest(http.MethodPost, "/api/v1/captcha/verify", bytes.NewBufferString(`{"id":"captcha-token-1","data":"ok"}`)) verifyReq := httptest.NewRequest(http.MethodPost, "/api/v1/captcha/verify", bytes.NewBufferString(`{"id":"captcha-token-1","data":"ok"}`))
verifyReq.Header.Set("Content-Type", "application/json") verifyReq.Header.Set("Content-Type", "application/json")