Documentation
¶
Overview ¶
Package captcha provides a small pluggable captcha verifier interface and a Cloudflare Turnstile implementation.
Implementations are stateless and safe for concurrent use. Failures are classified into typed sentinels so callers can implement fail-open vs fail-closed policy without parsing provider responses.
Index ¶
Constants ¶
const DefaultTurnstileTimeout = 5 * time.Second
DefaultTurnstileTimeout is the default HTTP timeout for the Turnstile verifier. Captcha verification sits on the auth path; we'd rather fail fast than hang the request.
Variables ¶
var ( ErrMissingToken = errors.New("captcha: missing token") ErrInvalidToken = errors.New("captcha: invalid token") ErrDuplicateToken = errors.New("captcha: token already used") ErrTransientFailure = errors.New("captcha: transient verifier failure") )
Common sentinel errors that map to recurring failure modes.
Functions ¶
This section is empty.
Types ¶
type Result ¶
type Result struct {
Success bool
ChallengeTS string // RFC3339 timestamp of the challenge solve
Hostname string // hostname of the challenge issuer
Action string // optional client-supplied action binding (echoed by provider)
ErrorCodes []string // provider-specific error codes (unused on success)
}
Result is the structured response from a verifier returned on success.
For the Turnstile verifier, ChallengeTS, Hostname, and Action are populated from the provider response and are suitable for audit-log enrichment. ErrorCodes is unused on success (failures return (nil, err) instead).
type TurnstileVerifier ¶
type TurnstileVerifier struct {
// contains filtered or unexported fields
}
TurnstileVerifier verifies Cloudflare Turnstile tokens.
It is stateless and safe for concurrent use. Construct via NewTurnstileVerifier; the zero value is not usable.
func NewTurnstileVerifier ¶
func NewTurnstileVerifier(secret string, httpClient *http.Client) *TurnstileVerifier
NewTurnstileVerifier returns a verifier configured with the given secret. httpClient may be nil: it defaults to a client with a 5s timeout, whose calls to Cloudflare are client spans under the request's span.
func (*TurnstileVerifier) Verify ¶
func (v *TurnstileVerifier) Verify(ctx context.Context, token, remoteIP, action string) (*Result, error)
Verify implements Verifier.
On success: returns (*Result, nil) with ChallengeTS, Hostname, and Action populated from the provider response. If the caller supplied a non-empty action and it does not match the response's action, returns (nil, *VerifyError{Codes: ["action-mismatch"]}).
On failure: returns (nil, err) — ErrMissingToken if token is empty; ErrTransientFailure on network/parse/non-2xx; otherwise the classified sentinel for known Turnstile error codes or a *VerifyError preserving the provider codes.
Note: Cloudflare's siteverify does NOT accept "action" as a request form field; the action is bound at widget render time and echoed back in the response. We therefore do not send it on the wire — we only compare the echoed value against what the caller expects.
type Verifier ¶
type Verifier interface {
// Verify returns a populated *Result and nil error on success. On failure
// it returns (nil, err) — typically a sentinel (ErrMissingToken,
// ErrInvalidToken, ErrDuplicateToken, ErrTransientFailure) or a wrapped
// *VerifyError carrying provider-specific codes.
//
// The remoteIP is the client's IP (can be empty if unknown). The action
// is an optional binding the caller expects: when non-empty, the verifier
// compares it against the action echoed back by the provider and returns
// *VerifyError{Codes: ["action-mismatch"]} if they differ.
Verify(ctx context.Context, token, remoteIP, action string) (*Result, error)
}
Verifier validates a captcha challenge token. Implementations are stateless and safe for concurrent use.
type VerifyError ¶
type VerifyError struct {
Codes []string
}
VerifyError is returned when a verifier rejects a token. Codes are the provider-specific reasons. Use errors.As to extract.