captchala

package module
v1.0.2 Latest Latest
Warning

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

Go to latest
Published: May 1, 2026 License: MIT Imports: 7 Imported by: 0

README

Captchala Go SDK

Server-side SDK for validating Captcha tokens.

中文文档

Installation

go get github.com/Captcha-La/captchala-go

Quick Start

package main

import (
    "fmt"
    "log"

    captchala "github.com/Captcha-La/captchala-go"
)

func main() {
    // Create client
    client := captchala.NewClient("your_app_key", "your_app_secret")

    // Validate token
    result, err := client.Validate(token)
    if err != nil {
        log.Printf("Validation error: %v", err)
    }

    if result.Valid {
        fmt.Println("Verification passed")
        if result.Offline {
            fmt.Println("This was an offline verification")
        }
    } else {
        fmt.Printf("Verification failed: %s\n", result.Error)
    }
}

API Reference

NewClient(appKey, appSecret string) *Client

Create a client with default 5-second timeout.

NewClientWithTimeout(appKey, appSecret string, timeout time.Duration) *Client

Create a client with custom timeout.

Client.Validate(token string) (*ValidateResult, error)

Validate and consume a token.

Client.ValidateWithOptions(token string, keepToken bool) (*ValidateResult, error)

Validate a token. If keepToken is true, the token won't be consumed and can be validated again.

Client.ValidateWithClientIP(token string, keepToken bool, clientIP string) (*ValidateResult, error)

Validate a token and forward the end-user IP for bind_ip verification. If the pass_token was issued with bind_ip, the backend compares this value against the bound IP and rejects mismatches. Pass the real user IP extracted from YOUR inbound request (e.g., the X-Forwarded-For head), not a proxy IP. When clientIP is empty the field is omitted from the request body — behaving identically to ValidateWithOptions.

result, err := client.ValidateWithClientIP(token, false, userIP)
ValidateResult
type ValidateResult struct {
    Valid       bool   // Whether validation passed
    Offline     bool   // Whether this was offline verification
    ClientOnly  bool   // Whether this is a client-only token
    ChallengeID string // Challenge ID
    Action      string // Business action
    UID         string // User ID bound via bind_uid (for server-side identity check)
    Error       string // Error message
    Warning     string // Warning message
}
Verifying bind_uid

If you issued the server_token with bind_uid = "user_42", check the result:

result, err := client.Validate(token)
if err == nil && result.Valid && result.UID != expectedUserID {
    // pass_token was issued for a different user — reject
}
Client.IssueServerToken(action string) (*IssueResult, error)

Mint a one-time sct_ server token with default options (no IP/UID binding, default TTL/maxUses).

Client.IssueServerTokenWithOptions(action string, opts IssueOptions) (*IssueResult, error)

Mint a server token with full control. Hand the returned Token to the browser SDK as the serverToken prop — single-use, action-scoped, optionally IP/UID-bound.

issue, err := client.IssueServerTokenWithOptions("login", captchala.IssueOptions{
    BindingIP: userIP,    // backend rejects if a different IP redeems
    TTL:       300,       // seconds
    MaxUses:   5,         // SDK retry budget
    BindUID:   userID,    // pair with ValidateResult.UID on verify
})
if err != nil || !issue.OK {
    http.Error(w, issue.Error, http.StatusBadRequest)
    return
}
// hand issue.Token to the browser
IssueResult / IssueOptions
type IssueResult struct {
    OK        bool
    Token     string  // sct_<hex>
    ExpiresIn int     // seconds
    IssuedAt  int64   // unix seconds
    Error     string
    Message   string
}

type IssueOptions struct {
    BindingIP string  // end-user IP for bind_ip enforcement
    TTL       int     // 0 → server default (300)
    MaxUses   int     // 0 → server default
    BindUID   string  // user ID; verify side compares ValidateResult.UID
}
Client.ModerationCheck(input []ModerationItem, userID string) (*ModerationResult, error)

Multi-modal content moderation. input is a slice of ModerationItem — text and image_url entries can be mixed in one call (OpenAI-compatible).

result, err := client.ModerationCheck([]captchala.ModerationItem{
    captchala.TextItem(userComment),
    captchala.ImageURLItem(uploadedImageURL),
}, userID)

if result.Flagged && result.HasCategory("violence", "csam") {
    // hard block
}
Client.ModerationText(text, userID string) (*ModerationResult, error)

Convenience wrapper for plain-text moderation.

result, err := client.ModerationText("user comment here", userID)
ModerationItem / ModerationResult
type ModerationItem struct {
    Type     string         // "text" or "image_url"
    Text     string         // for type="text"
    ImageURL map[string]any // for type="image_url" — {"url": "https://..."}
}

// Helpers — usually preferred over building items by hand:
captchala.TextItem("...")
captchala.ImageURLItem("https://...")

type ModerationResult struct {
    OK          bool
    Flagged     bool
    Categories  map[string]bool   // category name → tripped
    ContentType string            // "text" / "image" / "mixed"
    Raw         map[string]any    // full upstream payload
    Error       string
    Message     string
}

// Method:
result.HasCategory("violence", "csam")  // true if ANY of the names tripped

Token Types

Prefix Source Security Level
pt_ Main API High
offline_ Backup Service Medium
client_ Client-only Low (cannot verify server-side)

Complete Example

package main

import (
    "net/http"
    "os"

    "github.com/gin-gonic/gin"
    captchala "github.com/Captcha-La/captchala-go"
)

var captchaClient *captchala.Client

func init() {
    captchaClient = captchala.NewClient(
        os.Getenv("CAPTCHALA_APP_KEY"),
        os.Getenv("CAPTCHALA_APP_SECRET"),
    )
}

// CaptchaMiddleware validates captcha tokens
func CaptchaMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        token := c.PostForm("captcha_token")
        if token == "" {
            token = c.GetHeader("X-Captcha-Token")
        }

        if token == "" {
            c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
                "error": "missing_captcha_token",
            })
            return
        }

        result, _ := captchaClient.Validate(token)
        if !result.Valid {
            c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
                "error":   "captcha_failed",
                "message": result.Error,
            })
            return
        }

        // Pass validation info to handlers
        c.Set("captcha_offline", result.Offline)
        c.Set("captcha_client_only", result.ClientOnly)

        c.Next()
    }
}

func main() {
    r := gin.Default()

    r.POST("/login", CaptchaMiddleware(), func(c *gin.Context) {
        // Captcha passed, handle login
        c.JSON(http.StatusOK, gin.H{"message": "Login successful"})
    })

    r.Run(":8080")
}

Testing

# Run tests
go test -v

# Integration tests (requires real credentials)
CAPTCHALA_APP_KEY=xxx CAPTCHALA_APP_SECRET=xxx go test -v

# Benchmarks
go test -bench=.

License

MIT

Documentation

Overview

Package captchala provides a server-side SDK for validating Captcha tokens.

Usage:

import captchala "github.com/Captcha-La/captchala-go"

client := captchala.NewClient("your_app_key", "your_app_secret")
result, err := client.Validate(token)
if err != nil {
    // handle error
}
if result.Valid {
    // verification passed
}

Index

Constants

View Source
const (
	// MainAPIURL is the primary API endpoint for pt_ tokens
	MainAPIURL = "https://apiv1.captcha.la/v1/validate"
	// BackupAPIURL is the backup API endpoint for offline_ tokens
	BackupAPIURL = "https://fallbackapiv1.captchala.com/api/validate"
	// IssueAPIURL mints a one-time server_token (sct_) — POST /v1/server/challenge/issue
	IssueAPIURL = "https://apiv1.captcha.la/v1/server/challenge/issue"
	// ModerationCheckURL multi-modal content moderation — POST /v1/moderation/check
	ModerationCheckURL = "https://apiv1.captcha.la/v1/moderation/check"
	// ModerationTextURL plain-text moderation — POST /v1/moderation/text
	ModerationTextURL = "https://apiv1.captcha.la/v1/moderation/text"

	// Token prefixes
	PrefixMain    = "pt_"      // Main API token prefix
	PrefixOffline = "offline_" // Backup/offline token prefix
	PrefixClient  = "client_"  // Client-only token prefix
)

Variables

This section is empty.

Functions

This section is empty.

Types

type Client

type Client struct {
	AppKey    string
	AppSecret string
	Timeout   time.Duration
	// BaseURL overrides the main API URL. Mainly used for tests.
	// If empty, MainAPIURL is used.
	BaseURL string
	// BackupURL overrides the backup API URL. Mainly used for tests.
	// If empty, BackupAPIURL is used.
	BackupURL string
	// IssueURL / ModerationCheckURL / ModerationTextURL override the
	// corresponding endpoint URLs. Mainly used for tests; leave empty
	// in production.
	IssueURL              string
	ModerationCheckURLStr string
	ModerationTextURLStr  string
	// contains filtered or unexported fields
}

Client is the Captchala SDK client

func NewClient

func NewClient(appKey, appSecret string) *Client

NewClient creates a new Captchala client with default timeout (5 seconds)

func NewClientWithTimeout

func NewClientWithTimeout(appKey, appSecret string, timeout time.Duration) *Client

NewClientWithTimeout creates a new Captchala client with custom timeout

func (*Client) IssueServerToken added in v1.0.2

func (c *Client) IssueServerToken(action string) (*IssueResult, error)

IssueServerToken mints a one-time server_token for the given action. Hand the returned sct_ token to the browser SDK via the serverToken prop.

func (*Client) IssueServerTokenWithOptions added in v1.0.2

func (c *Client) IssueServerTokenWithOptions(action string, opts IssueOptions) (*IssueResult, error)

IssueServerTokenWithOptions is the same as IssueServerToken but lets you bind the token to an IP / UID and override TTL / max_uses.

func (*Client) ModerationCheck added in v1.0.2

func (c *Client) ModerationCheck(input []ModerationItem, userID string) (*ModerationResult, error)

ModerationCheck does multi-modal content moderation. Pass a slice of ModerationItem (text and/or image_url entries). userID is optional.

func (*Client) ModerationText added in v1.0.2

func (c *Client) ModerationText(text, userID string) (*ModerationResult, error)

ModerationText is a convenience wrapper for plain-text moderation.

func (*Client) Validate

func (c *Client) Validate(token string) (*ValidateResult, error)

Validate validates a captcha token and consumes it

func (*Client) ValidateWithClientIP added in v1.0.2

func (c *Client) ValidateWithClientIP(token string, keepToken bool, clientIP string) (*ValidateResult, error)

ValidateWithClientIP validates with explicit client IP (for bind_ip check). Pass the real user IP from YOUR request, not the server-side proxy IP. If the pass_token was issued with bind_ip, the backend will compare the provided client_ip against the bound IP and reject mismatches.

func (*Client) ValidateWithOptions

func (c *Client) ValidateWithOptions(token string, keepToken bool) (*ValidateResult, error)

ValidateWithOptions validates a captcha token with options. If keepToken is true, the token will not be consumed and can be validated again.

type IssueOptions added in v1.0.2

type IssueOptions struct {
	BindingIP string // End-user IP — backend rejects token if a different IP redeems it
	TTL       int    // Lifetime in seconds; server enforces a hard upper bound
	MaxUses   int    // SDK retry budget; verification is still single-pass
	BindUID   string // User ID to bind; pair with ValidateResult.UID on verify side
}

IssueOptions are optional knobs for IssueServerTokenWithOptions.

type IssueResult added in v1.0.2

type IssueResult struct {
	OK        bool   `json:"ok"`
	Token     string `json:"token,omitempty"`      // sct_<hex>; pass to browser SDK as serverToken
	ExpiresIn int    `json:"expires_in,omitempty"` // seconds
	IssuedAt  int64  `json:"issued_at,omitempty"`  // unix seconds
	Error     string `json:"error,omitempty"`
	Message   string `json:"message,omitempty"`
}

IssueResult contains the server_token issuance result.

type ModerationItem added in v1.0.2

type ModerationItem struct {
	Type     string         `json:"type"` // "text" or "image_url"
	Text     string         `json:"text,omitempty"`
	ImageURL map[string]any `json:"image_url,omitempty"` // {"url": "https://..."}
}

ModerationItem is one entry in a multi-modal moderation request. Use either Text (with Type="text") or ImageURL (with Type="image_url").

func ImageURLItem added in v1.0.2

func ImageURLItem(url string) ModerationItem

ImageURLItem returns a {type:image_url, image_url:{url}} item.

func TextItem added in v1.0.2

func TextItem(text string) ModerationItem

TextItem returns a {type:text, text} item ready for ModerationCheck input.

type ModerationResult added in v1.0.2

type ModerationResult struct {
	OK          bool            `json:"ok"`
	Flagged     bool            `json:"flagged"`
	Categories  map[string]bool `json:"categories,omitempty"`   // category name → tripped
	ContentType string          `json:"content_type,omitempty"` // "text" / "image" / "mixed"
	Raw         map[string]any  `json:"raw,omitempty"`          // full upstream payload
	Error       string          `json:"error,omitempty"`
	Message     string          `json:"message,omitempty"`
}

ModerationResult contains the moderation verdict.

func (*ModerationResult) HasCategory added in v1.0.2

func (r *ModerationResult) HasCategory(names ...string) bool

HasCategory reports true if any of the named categories tripped.

type ValidateResult

type ValidateResult struct {
	// Valid indicates if the token is valid
	Valid bool `json:"valid"`
	// Offline indicates if this was an offline verification
	Offline bool `json:"offline"`
	// ClientOnly indicates if this is a client-only token (cannot be verified server-side)
	ClientOnly bool `json:"client_only"`
	// ChallengeID is the challenge identifier
	ChallengeID string `json:"challenge_id,omitempty"`
	// Action is the business action associated with the token
	Action string `json:"action,omitempty"`
	// UID is the user ID bound via bind_uid at server_token issuance time.
	// Use this to verify the pass_token was issued for the expected user.
	UID string `json:"uid,omitempty"`
	// Error contains the error message if validation failed
	Error string `json:"error,omitempty"`
	// Warning contains warning message (e.g., for client-only tokens)
	Warning string `json:"warning,omitempty"`
}

ValidateResult contains the validation result

Jump to

Keyboard shortcuts

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