mirror of
https://github.com/yusing/godoxy.git
synced 2026-04-20 23:41:23 +02:00
docs: add per package README for implementation details (AI generated with human review)
This commit is contained in:
264
internal/net/gphttp/middleware/captcha/README.md
Normal file
264
internal/net/gphttp/middleware/captcha/README.md
Normal file
@@ -0,0 +1,264 @@
|
||||
# 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
|
||||
|
||||
```mermaid
|
||||
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
|
||||
|
||||
```mermaid
|
||||
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
|
||||
|
||||
```go
|
||||
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
|
||||
|
||||
```go
|
||||
type ProviderBase struct {
|
||||
Expiry time.Duration `json:"session_expiry"` // Default: 24 hours
|
||||
}
|
||||
|
||||
func (p *ProviderBase) SessionExpiry() time.Duration
|
||||
```
|
||||
|
||||
### hCAPTCHA Provider
|
||||
|
||||
```go
|
||||
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
|
||||
|
||||
```go
|
||||
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
|
||||
|
||||
```go
|
||||
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
|
||||
|
||||
```go
|
||||
func PreRequest(p Provider, w http.ResponseWriter, r *http.Request) (proceed bool)
|
||||
```
|
||||
|
||||
This function:
|
||||
|
||||
1. Checks for valid session cookie
|
||||
1. Validates session expiry
|
||||
1. Returns true if session is valid
|
||||
1. For non-HTML requests, returns 403 Forbidden
|
||||
1. For POST requests, verifies the captcha token
|
||||
1. For GET requests, renders the CAPTCHA challenge page
|
||||
|
||||
## Configuration
|
||||
|
||||
### hCAPTCHA Configuration
|
||||
|
||||
```yaml
|
||||
middleware:
|
||||
my-captcha:
|
||||
use: hcaptcha
|
||||
site_key: "YOUR_SITE_KEY"
|
||||
secret: "YOUR_SECRET"
|
||||
session_expiry: 24h # optional, default 24h
|
||||
```
|
||||
|
||||
### Route Configuration
|
||||
|
||||
```yaml
|
||||
routes:
|
||||
- host: example.com
|
||||
path: /admin
|
||||
middlewares:
|
||||
- my-captcha
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Basic Setup
|
||||
|
||||
```go
|
||||
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
|
||||
|
||||
```yaml
|
||||
# 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_session` with HttpOnly flag
|
||||
|
||||
```mermaid
|
||||
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:
|
||||
|
||||
```go
|
||||
// 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
|
||||
|
||||
1. **Session Cookie**: Uses HttpOnly flag to prevent JavaScript access
|
||||
1. **Token Verification**: Tokens are verified server-side with the CAPTCHA provider
|
||||
1. **Remote IP**: Client IP is included in verification request to prevent token reuse
|
||||
1. **Session Expiry**: Sessions expire after configurable duration
|
||||
1. **Non-HTML Fallback**: Non-HTML requests receive 403 without challenge page
|
||||
|
||||
## Error Handling
|
||||
|
||||
```go
|
||||
var ErrCaptchaVerificationFailed = gperr.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 `jsonstore` for persistent session storage
|
||||
- **Middleware Framework**: Implements `RequestModifier` interface
|
||||
Reference in New Issue
Block a user