Skip to content

feat: add Turnstile CAPTCHA support for login#382

Open
Hzend wants to merge 3 commits into
maillab:mainfrom
Hzend:main
Open

feat: add Turnstile CAPTCHA support for login#382
Hzend wants to merge 3 commits into
maillab:mainfrom
Hzend:main

Conversation

@Hzend
Copy link
Copy Markdown

@Hzend Hzend commented May 13, 2026

Summary

Add Turnstile human verification for the /api/login endpoint to protect against brute-force attacks.

Motivation

Currently, the login endpoint (POST /api/login) has no CAPTCHA protection, making it vulnerable to automated brute-force attacks. This PR extends the existing Turnstile verification infrastructure (already used for registration) to also cover login.

Changes

Backend

  • Database: Add login_verify and login_verify_count columns to setting table
  • Migration: Add login_verify and login_verify_count fields to v3_1DB() in init.js
  • Entity: Add verifyRecordType.LOGIN = 2
  • Service:
    • verify-record-service.js: Add isOpenLoginVerify, increaseLoginCount, clearLoginCount
    • login-service.js: Verify Turnstile token before password check; increment failure count on error; clear on success
    • setting-service.js: Return loginVerifyOpen in get() and websiteConfig()

Frontend

  • Login page: Conditionally render Turnstile widget based on loginVerify config
  • API: Pass token param in login() request
  • Admin settings: Add "Login Verification" config UI (same pattern as Register Verification)
  • i18n: Add loginVerification key (zh/en)

Verification Modes

Mode Value Behavior
Always ON 0 Always require CAPTCHA
Always OFF 1 Never require CAPTCHA (default)
After N failures 2 Require CAPTCHA after loginVerifyCount failures from same IP

Backwards Compatibility

  • Default login_verify = 1 (CLOSE): existing users are unaffected
  • /login accepts optional token param: old clients work without changes
  • New DB columns use DEFAULT values: safe for existing deployments

Testing

  • Login without CAPTCHA works when loginVerify = CLOSE
  • Login with CAPTCHA works when loginVerify = OPEN
  • COUNT mode triggers CAPTCHA after N failures
  • Login success clears failure count
  • Admin settings page correctly saves and displays config

Harvey added 2 commits May 13, 2026 21:38
- Reuse existing registration verification infrastructure
- Support three modes: Always ON / Always OFF / After N failures
- Default disabled (login_verify=1), no impact on existing users
- Track login failures per IP, clear on successful login
- OAuth login bypasses verification (consistent with register behavior)

后端:
- entity/setting.js: add login_verify, login_verify_count columns
- entity-const.js: add verifyRecordType.LOGIN = 2
- verify-record-service.js: add isOpenLoginVerify, increaseLoginCount, clearLoginCount
- login-service.js: verify Turnstile before password check; increment/clear failure count
- setting-service.js: return loginVerifyOpen in get() and websiteConfig()
- init.js: add v3_1DB() migration for schema upgrade

前端:
- request/login.js: pass token param
- login/index.vue: conditionally render Turnstile widget
- sys-setting/index.vue: add Login Verification config UI
- i18n zh/en: add loginVerification translation key
# Conflicts:
#	mail-worker/src/init/init.js
@Hzend Hzend closed this May 14, 2026
@Hzend Hzend reopened this May 14, 2026
- Change login widget class from .register-turnstile to .login-turnstile
- Add independent loginTurnstileId, loginBotJsError, loginVerifyErrorCount
- Add onLoginTurnstileSuccess/Error callbacks
- Prevent collision with registration Turnstile widget
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant