mirror of
https://github.com/yusing/godoxy.git
synced 2026-02-22 18:37:46 +01:00
This is a large-scale refactoring across the codebase that replaces the custom `gperr.Error` type with Go's standard `error` interface. The changes include: - Replacing `gperr.Error` return types with `error` in function signatures - Using `errors.New()` and `fmt.Errorf()` instead of `gperr.New()` and `gperr.Errorf()` - Using `%w` format verb for error wrapping instead of `.With()` method - Replacing `gperr.Subject()` calls with `gperr.PrependSubject()` - Converting error logging from `gperr.Log*()` functions to zerolog's `.Err().Msg()` pattern - Update NewLogger to handle multiline error message - Updating `goutils` submodule to latest commit This refactoring aligns with Go idioms and removes the dependency on custom error handling abstractions in favor of standard library patterns.
Captcha Middleware
CAPTCHA verification middleware package providing session-based captcha challenge and verification.
Overview
This package implements CAPTCHA verification middleware that protects routes by requiring users to complete a CAPTCHA challenge before accessing the protected resource. It supports pluggable providers (currently hCAPTCHA) and uses encrypted sessions for verification state.
Architecture
graph TD
A[Client Request] --> B{Captcha Session?}
B -->|Valid| C[Proceed to Backend]
B -->|Invalid| D[Show CAPTCHA Page]
D --> E{POST with Token?}
E -->|Valid| F[Create Session<br/>Set Cookie]
E -->|Invalid| G[Show Error]
F --> C
subgraph Captcha Provider
H[hCAPTCHA API]
D -->|Script/Form HTML| H
F -->|Verify Token| H
end
subgraph Session Store
I[CaptchaSessions<br/>jsonstore]
end
F --> I
I -.->|Session Check| B
Captcha Flow
sequenceDiagram
participant C as Client
participant M as Middleware
participant P as Provider
participant S as Session Store
participant B as Backend
C->>M: Request (no session)
M->>M: Check cookie
M->>M: Session not found/expired
M->>C: Send CAPTCHA Page
C->>M: POST with captcha response
M->>P: Verify token
P-->>M: Verification result
alt Verification successful
M->>S: Store session
M->>C: Set session cookie<br/>Redirect to protected path
C->>M: Request (with session cookie)
M->>S: Validate session
M->>B: Forward request
else Verification failed
M->>C: Error: verification failed
end
Core Components
Provider Interface
type Provider interface {
// CSP directives for the captcha provider
CSPDirectives() []string
// CSP sources for the captcha provider
CSPSources() []string
// Verify the captcha response from the request
Verify(r *http.Request) error
// Session expiry duration after successful verification
SessionExpiry() time.Duration
// Script HTML to include in the page
ScriptHTML() string
// Form HTML to render the captcha widget
FormHTML() string
}
ProviderBase
type ProviderBase struct {
Expiry time.Duration `json:"session_expiry"` // Default: 24 hours
}
func (p *ProviderBase) SessionExpiry() time.Duration
hCAPTCHA Provider
type HcaptchaProvider struct {
ProviderBase
SiteKey string `json:"site_key" validate:"required"`
Secret string `json:"secret" validate:"required"`
}
// CSP Directives: script-src, frame-src, style-src, connect-src
// CSP Sources: https://hcaptcha.com, https://*.hcaptcha.com
Captcha Session
type CaptchaSession struct {
ID string `json:"id"`
Expiry time.Time `json:"expiry"`
}
var CaptchaSessions = jsonstore.Store[*CaptchaSession]("captcha_sessions")
func newCaptchaSession(p Provider) *CaptchaSession
func (s *CaptchaSession) expired() bool
Middleware Integration
type hCaptcha struct {
captcha.HcaptchaProvider
}
func (h *hCaptcha) before(w http.ResponseWriter, r *http.Request) bool {
return captcha.PreRequest(h, w, r)
}
var HCaptcha = NewMiddleware[hCaptcha]()
PreRequest Handler
func PreRequest(p Provider, w http.ResponseWriter, r *http.Request) (proceed bool)
This function:
- Checks for valid session cookie
- Validates session expiry
- Returns true if session is valid
- For non-HTML requests, returns 403 Forbidden
- For POST requests, verifies the captcha token
- For GET requests, renders the CAPTCHA challenge page
Configuration
hCAPTCHA Configuration
middleware:
my-captcha:
use: hcaptcha
site_key: "YOUR_SITE_KEY"
secret: "YOUR_SECRET"
session_expiry: 24h # optional, default 24h
Route Configuration
routes:
- host: example.com
path: /admin
middlewares:
- my-captcha
Usage Examples
Basic Setup
import "github.com/yusing/godoxy/internal/net/gphttp/middleware"
hcaptchaMiddleware := middleware.HCaptcha.New(middleware.OptionsRaw{
"site_key": "your-site-key",
"secret": "your-secret",
})
Using in Middleware Chain
# config/middlewares/admin-protection.yml
- use: captcha
site_key: "${HCAPTCHA_SITE_KEY}"
secret: "${HCAPTCHA_SECRET}"
bypass:
- type: CIDR
value: 10.0.0.0/8
Session Management
Sessions are stored in a JSON-based store with the following properties:
- Session ID: 32-byte CRNG (
crypto/rand.Read) random hex string - Expiry: Configurable duration (default 24 hours)
- Cookie:
godoxy_captcha_sessionwith HttpOnly flag
flowchart TD
A[Session Created] --> B[Cookie Set]
B --> C[Client Sends Cookie]
C --> D{Session Valid?}
D -->|Yes| E[Proceed]
D -->|No| F{HTML Request?}
F -->|Yes| G[Show CAPTCHA]
F -->|No| H[403 Forbidden]
CSP Integration
The CAPTCHA provider supplies CSP directives that should be added to the response:
// hCAPTCHA CSP Directives
CSPDirectives() []string
// Returns: ["script-src", "frame-src", "style-src", "connect-src"]
CSPSources() []string
// Returns: ["https://hcaptcha.com", "https://*.hcaptcha.com"]
HTML Template
The package includes an embedded HTML template (captcha.html) that renders the CAPTCHA challenge page with:
- Provider script (
<script src="https://js.hcaptcha.com/1/api.js">) - Provider form (
<div class="h-captcha" data-sitekey="...">) - Auto-submit callback on successful verification
Security Considerations
- Session Cookie: Uses HttpOnly flag to prevent JavaScript access
- Token Verification: Tokens are verified server-side with the CAPTCHA provider
- Remote IP: Client IP is included in verification request to prevent token reuse
- Session Expiry: Sessions expire after configurable duration
- Non-HTML Fallback: Non-HTML requests receive 403 without challenge page
Error Handling
var ErrCaptchaVerificationFailed = errors.New("captcha verification failed")
// Verification errors are logged with request details
log.Warn().Err(err).Str("url", r.URL.String()).Str("remote_addr", r.RemoteAddr).Msg("failed to verify captcha")
Integration with GoDoxy
The captcha middleware integrates with GoDoxy's:
- Authentication: Sessions are managed via
auth.SetTokenCookie - Session Store: Uses
jsonstorefor persistent session storage - Middleware Framework: Implements
RequestModifierinterface