Help us shape the future of Go! Take the 2026 Go Developer Survey

challenge

package
v0.33.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 29, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package challenge stops bots on form routes (signup, login, contact) with a challenge the browser solves: Cloudflare Turnstile and hCaptcha through SiteVerify, or any Verifier of your own.

Middleware verifies unsafe requests only. A missing or rejected answer is a validation error on the "challenge" field (422, or a redirect back with errors for HTML forms); a provider that cannot be reached denies with 503 (fail closed). Both log a security event. Pass When to ask only suspicious clients, for example after a soft rate limit trips.

verify := challenge.Turnstile(cfg.TurnstileSecret)
g.Use(challenge.Middleware(verify, nil)) // on the group holding POST /register

See https://github.com/AlexGalhardo/vukem/blob/main/docs/subsystems.md#bot-challenge.

Index

Examples

Constants

View Source
const Field = "challenge"

Field is the validation-error field of a failed challenge.

View Source
const Header = "X-Challenge-Token"

Header carries the answer for JSON and Inertia requests, which do not post the widget's hidden form field.

Variables

View Source
var ErrRejected = errors.New("challenge: rejected")

ErrRejected is returned by a Verifier when the answer is missing or wrong. Any other error means the check could not run.

Functions

func Middleware

func Middleware(v Verifier, when func(c *web.Ctx) bool) func(http.Handler) http.Handler

Middleware verifies the challenge on POST, PUT, PATCH and DELETE requests for which when returns true (nil: all of them); safe methods pass, so the page that renders the widget is not challenged.

Example
package main

import (
	"net/http"

	"github.com/AlexGalhardo/vukem/challenge"
	"github.com/AlexGalhardo/vukem/config"
	"github.com/AlexGalhardo/vukem/router"
	"github.com/AlexGalhardo/vukem/web"
)

func main() {
	verify := challenge.Turnstile(config.NewSecret("turnstile-secret"))
	verify.Hostname = "example.com"

	r := router.New(&web.Services{})
	r.Group("", func(g *router.Group) {
		g.Use(challenge.Middleware(verify, nil))
		g.Post("/register", func(c *web.Ctx) error { return c.String(http.StatusCreated, "welcome") })
	})
}

Types

type SiteVerify

type SiteVerify struct {
	// URL is the siteverify endpoint.
	URL string
	// Secret is the server-side secret key.
	Secret config.Secret
	// FormField is the form field the widget fills in (the Header is also
	// read, for JSON requests).
	FormField string
	// Hostname, when set, must equal the hostname the provider reports the
	// widget was solved on, so a token solved on another site is refused.
	Hostname string
	// Client sends the request; nil uses httpclient.Safe with a 5s timeout.
	Client *http.Client
}

SiteVerify verifies tokens with a siteverify endpoint, the protocol of Cloudflare Turnstile and hCaptcha: the secret, the token and the client IP are posted, and the JSON answer must say success.

func HCaptcha

func HCaptcha(secret config.Secret) *SiteVerify

HCaptcha returns an hCaptcha verifier.

func Turnstile

func Turnstile(secret config.Secret) *SiteVerify

Turnstile returns a Cloudflare Turnstile verifier.

func (*SiteVerify) Verify

func (s *SiteVerify) Verify(c *web.Ctx) error

Verify implements Verifier.

type Verifier

type Verifier interface {
	// Verify returns nil for a valid answer, an error wrapping ErrRejected
	// for a missing or wrong one, and any other error when it cannot tell.
	Verify(c *web.Ctx) error
}

Verifier checks the challenge answer carried by the request.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL