### Install go-turnstile
Source: https://github.com/tordrt/go-turnstile/blob/main/README.md
Install the library using the go get command.
```bash
go get github.com/tordrt/go-turnstile
```
--------------------------------
### Quick Start: Basic Server Setup
Source: https://github.com/tordrt/go-turnstile/blob/main/README.md
Sets up a simple HTTP server to verify Turnstile tokens from incoming requests. Requires your Cloudflare site and secret keys.
```go
package main
import (
"fmt"
"log"
"net/http"
"github.com/tordrt/go-turnstile"
)
func main() {
// Create a new Turnstile client
tturnstileClient, err := turnstile.New("your-site-key", "your-secret-key")
if err != nil {
log.Fatal(err)
}
http.HandleFunc("/verify", func(w http.ResponseWriter, r *http.Request) {
// Verify the token from the HTTP request
response, err := turnstileClient.VerifyRequest(r.Context(), r)
if err != nil {
http.Error(w, "CAPTCHA verification failed", http.StatusBadRequest)
return
}
fmt.Fprintf(w, "Verification successful! Hostname: %s", response.Hostname)
})
log.Println("Server starting on :8080")
http.ListenAndServe(":8080", nil)
}
```
--------------------------------
### Quick Start - Testing Handler with VerifyRequest
Source: https://github.com/tordrt/go-turnstile/blob/main/README.md
Demonstrates how to test handlers using `VerifyRequest` with the `go-turnstile` test client. It sets up a test client, creates a test request with data, and verifies the request.
```go
import (
"testing"
"context"
"github.com/tordrt/go-turnstile"
)
func TestMyHandler(t *testing.T) {
// Create a test client that always passes verification
client := turnstile.NewTestClient()
// Create a test request with the Turnstile token already included
req := turnstile.NewTestRequest(map[string]string{
"username": "testuser",
})
// Verify the request
response, err := client.VerifyRequest(context.Background(), req)
if err != nil {
t.Fatalf("Expected successful verification: %v", err)
}
// Test your handler logic with a valid token
if !response.Success {
t.Error("Expected successful response")
}
}
```
--------------------------------
### Quick Start - Testing Direct Token Verification
Source: https://github.com/tordrt/go-turnstile/blob/main/README.md
Shows how to test direct token verification using the `go-turnstile` test client. This is useful for testing the `VerifyToken` function directly.
```go
func TestTokenVerification(t *testing.T) {
client := turnstile.NewTestClient()
response, err := client.VerifyToken(context.Background(), turnstile.TestToken)
if err != nil {
t.Fatalf("Expected successful verification: %v", err)
}
}
```
--------------------------------
### Complete Turnstile Handler Test Example
Source: https://github.com/tordrt/go-turnstile/blob/main/README.md
This example demonstrates a comprehensive test suite for a Turnstile handler, utilizing different test client configurations to simulate various verification outcomes like success, failure, and duplicate tokens.
```go
func TestTurnstileHandler(t *testing.T) {
tests := []struct {
name string
client *turnstile.Client
expectError bool
}{
{
name: "successful verification",
client: turnstile.NewTestClient(),
expectError: false,
},
{
name: "failed verification",
client: turnstile.NewTestClientAlwaysFail(),
expectError: true,
},
{
name: "duplicate token",
client: turnstile.NewTestClientTokenSpent(),
expectError: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
response, err := tt.client.VerifyToken(
context.Background(),
turnstile.TestToken,
)
if tt.expectError && err == nil {
t.Error("Expected error but got none")
}
if !tt.expectError && err != nil {
t.Errorf("Unexpected error: %v", err)
}
})
}
}
```
--------------------------------
### HTML Form for Turnstile Integration
Source: https://github.com/tordrt/go-turnstile/blob/main/README.md
An example of a complete HTML form that integrates the Cloudflare Turnstile widget. Ensure the 'cf-turnstile' div has your site key and the script is correctly included.
```html
Turnstile Demo
```
--------------------------------
### New - Create a Turnstile client
Source: https://context7.com/tordrt/go-turnstile/llms.txt
Creates a new `*Client` configured with a site key and secret key. Accepts optional `ClientOption` functions for further configuration. Returns an error if either key is empty or whitespace.
```APIDOC
## New
Creates a new `*Client` configured with a site key and secret key. Accepts optional `ClientOption` functions for further configuration. Returns an error if either key is empty or whitespace.
### Method Signature
```go
func New(siteKey, secretKey string, options ...ClientOption) (*Client, error)
```
### Parameters
- **siteKey** (string) - Required - The Cloudflare Turnstile site key.
- **secretKey** (string) - Required - The Cloudflare Turnstile secret key.
- **options** ([]ClientOption) - Optional - Functional options to customize the client, such as `WithMaxRetries`, `WithRetryDelay`, `WithVerifyEndpoint`, and `WithHTTPClient`.
### Returns
- **(*Client, error)** - A pointer to the initialized `Client` or an error if the keys are invalid or options are misconfigured.
```
--------------------------------
### Create Integration Test Clients for Turnstile
Source: https://context7.com/tordrt/go-turnstile/llms.txt
Use these functions to create real `*Client` instances for integration tests. They use dummy Cloudflare keys and make actual HTTP requests.
```go
package myhandler_test
import (
"context"
"errors"
"testing"
"github.com/tordrt/go-turnstile"
)
func TestVerify_HappyPath(t *testing.T) {
client := turnstile.NewTestClient()
resp, err := client.VerifyToken(context.Background(), turnstile.TestToken)
if err != nil {
t.Fatalf("expected success, got: %v", err)
}
if !resp.Success {
t.Error("expected Success=true")
}
}
```
```go
func TestVerify_AlwaysFail(t *testing.T) {
client := turnstile.NewTestClientAlwaysFail()
_, err := client.VerifyToken(context.Background(), turnstile.TestToken)
if err == nil {
t.Fatal("expected failure but got nil error")
}
}
```
```go
func TestVerify_TokenSpent(t *testing.T) {
client := turnstile.NewTestClientTokenSpent()
_, err := client.VerifyToken(context.Background(), turnstile.TestToken)
var dupErr *turnstile.ErrTimeoutOrDuplicate
if !errors.As(err, &dupErr) {
t.Fatalf("expected ErrTimeoutOrDuplicate, got %T: %v", err, err)
}
}
```
--------------------------------
### Create Turnstile Client with Defaults
Source: https://context7.com/tordrt/go-turnstile/llms.txt
Create a new Turnstile client with default settings for timeout and retries. Ensure site and secret keys are valid.
```go
package main
import (
"log"
"net/http"
"time"
"github.com/tordrt/go-turnstile"
)
func main() {
// Minimal client with defaults (3s timeout, 2 retries, 100ms retry delay)
client, err := turnstile.New("0x4AAAAAAA_sitekey", "0x4AAAAAAA_secretkey")
if err != nil {
log.Fatal(err) // ErrInvalidSiteKey or ErrInvalidSecretKey
}
_ = client
}
```
--------------------------------
### Configure go-turnstile Client with Functional Options
Source: https://context7.com/tordrt/go-turnstile/llms.txt
Shows how to create a `turnstile.Client` with custom configurations using functional options. This includes setting a custom HTTP client with specific transport settings, overriding the verification endpoint, and configuring retry behavior like maximum retries and initial retry delay.
```go
package main
import (
"crypto/tls"
"log"
"net/http"
"time"
"github.com/tordrt/go-turnstile"
)
func main() {
// Custom transport (e.g., behind a corporate proxy with custom TLS)
transport := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: false},
MaxIdleConns: 20,
IdleConnTimeout: 60 * time.Second,
}
client, err := turnstile.New(
"0x4AAAAAAA_sitekey",
"0x4AAAAAAA_secretkey",
turnstile.WithHTTPClient(&http.Client{
Timeout: 10 * time.Second,
Transport: transport,
}),
// Override endpoint (e.g., staging or enterprise proxy)
turnstile.WithVerifyEndpoint("https://challenges.cloudflare.com/turnstile/v0/siteverify"),
// Retry up to 5 times for transient failures
turnstile.WithMaxRetries(5),
// Start at 50ms, doubling each attempt: 50ms → 100ms → 200ms → ...
turnstile.WithRetryDelay(50*time.Millisecond),
)
if err != nil {
log.Fatal(err)
}
_ = client
}
```
--------------------------------
### Create Mock Clients for Turnstile Unit Tests
Source: https://context7.com/tordrt/go-turnstile/llms.txt
Use these functions to create mock `*Client` instances for offline unit tests. They return a `*Client` and a `*MockRoundTripper` to inspect HTTP calls without network traffic.
```go
package myhandler_test
import (
"context"
"errors"
"testing"
"github.com/tordrt/go-turnstile"
)
func TestMock_Success(t *testing.T) {
client, mock := turnstile.NewMockClient()
resp, err := client.VerifyToken(context.Background(), "any-token")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !resp.Success {
t.Error("expected Success=true")
}
if mock.CallCount() != 1 {
t.Errorf("expected 1 HTTP call, got %d", mock.CallCount())
}
}
```
```go
func TestMock_AlwaysFail(t *testing.T) {
client, _ := turnstile.NewMockClientAlwaysFail()
_, err := client.VerifyToken(context.Background(), "any-token")
var invalidErr *turnstile.ErrInvalidInputResponse
if !errors.As(err, &invalidErr) {
t.Fatalf("expected ErrInvalidInputResponse, got %T", err)
}
}
```
```go
func TestMock_TokenSpent(t *testing.T) {
client, _ := turnstile.NewMockClientTokenSpent()
_, err := client.VerifyToken(context.Background(), "any-token")
var dupErr *turnstile.ErrTimeoutOrDuplicate
if !errors.As(err, &dupErr) {
t.Fatalf("expected ErrTimeoutOrDuplicate, got %T", err)
}
}
```
--------------------------------
### Inspect Turnstile Verification Response Structure
Source: https://context7.com/tordrt/go-turnstile/llms.txt
Demonstrates how to access and print details from the `Response` struct returned by `VerifyToken`. Includes fields like Success, ChallengeTS, Hostname, Action, CData, and ErrorCodes. Also shows how to access Enterprise-specific metadata.
```go
package main
import (
"context"
"fmt"
"log"
"github.com/tordrt/go-turnstile"
)
func inspectResponse(client *turnstile.Client, token string) {
resp, err := client.VerifyToken(context.Background(), token)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Success: %v\n", resp.Success)
fmt.Printf("ChallengeTS: %s\n", resp.ChallengeTS) // ISO 8601, e.g. "2024-06-15T12:00:00Z"
fmt.Printf("Hostname: %s\n", resp.Hostname) // e.g. "myapp.example.com"
fmt.Printf("Action: %s\n", resp.Action) // widget action name from Cloudflare dashboard
fmt.Printf("CData: %s\n", resp.CData) // customer data passed to widget
fmt.Printf("ErrorCodes: %v\n", resp.ErrorCodes) // non-empty only when Success is false
// Enterprise only
if resp.Metadata != nil {
fmt.Printf("EphemeralID: %s\n", resp.Metadata.EphemeralID)
}
}
```
--------------------------------
### Unit Test with Mock Turnstile Client (No Network Calls)
Source: https://github.com/tordrt/go-turnstile/blob/main/README.md
Use `NewMockClient` for true unit testing without any network requests. This mock client allows you to fully control responses and verify interactions without external dependencies.
```go
func TestMyHandler(t *testing.T) {
// Create a mock client - NO HTTP requests are made
client, mock := turnstile.NewMockClient()
// Any token works - responses are fully mocked
response, err := client.VerifyToken(context.Background(), "any-token")
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
// Verify the mock was called
if mock.CallCount() != 1 {
t.Errorf("Expected 1 call, got %d", mock.CallCount())
}
// Inspect the request that was made
body := mock.LastRequestBody()
// ... assertions on the request
}
```
--------------------------------
### Create Fully Customized Turnstile Client
Source: https://context7.com/tordrt/go-turnstile/llms.txt
Create a Turnstile client with custom configurations for retries, retry delay, verification endpoint, and HTTP client settings. This allows fine-grained control over the client's behavior.
```go
package main
import (
"log"
"net/http"
"time"
"github.com/tordrt/go-turnstile"
)
func main() {
// Fully customized client
client, err := turnstile.New(
"0x4AAAAAAA_sitekey",
"0x4AAAAAAA_secretkey",
turnstile.WithMaxRetries(3),
turnstile.WithRetryDelay(200*time.Millisecond),
turnstile.WithVerifyEndpoint("https://challenges.cloudflare.com/turnstile/v0/siteverify"),
turnstile.WithHTTPClient(&http.Client{
Timeout: 5 * time.Second,
Transport: &http.Transport{
MaxIdleConns: 10,
IdleConnTimeout: 30 * time.Second,
},
}),
)
if err != nil {
log.Fatal(err)
}
_ = client
}
```
--------------------------------
### Test Dynamic Mock Behavior Change in Go
Source: https://github.com/tordrt/go-turnstile/blob/main/README.md
Demonstrates changing mock client behavior dynamically using `SetResponse`. The first call succeeds, and a subsequent call fails after the mock's response is updated.
```go
func TestDynamicBehavior(t *testing.T) {
client, mock := turnstile.NewMockClient()
// First call succeeds
_, err := client.VerifyToken(context.Background(), "token")
if err != nil {
t.Fatal("Expected success")
}
// Change to failure mode
mock.SetResponse(turnstile.MockResponse{
Success: false,
ErrorCodes: []string{"timeout-or-duplicate"},
})
// Second call fails
_, err = client.VerifyToken(context.Background(), "token")
if err == nil {
t.Fatal("Expected failure")
}
}
```
--------------------------------
### NewMockClientWithResponse - Custom Mock Response
Source: https://context7.com/tordrt/go-turnstile/llms.txt
Use this to provide complete control over the mock's returned Response fields. This is useful when your application logic depends on specific metadata like Action, Hostname, or ChallengeTS.
```go
package myhandler_test
import (
"context"
"testing"
"github.com/tordrt/go-turnstile"
)
func TestMock_CustomResponse(t *testing.T) {
client, mock := turnstile.NewMockClientWithResponse(turnstile.MockResponse{
Success: true,
ChallengeTS: "2024-06-15T12:00:00Z",
Hostname: "myapp.example.com",
Action: "login",
CData: "user-id-42",
})
resp, err := client.VerifyToken(context.Background(), "token")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if resp.Action != "login" {
t.Errorf("expected action 'login', got %q", resp.Action)
}
if resp.Hostname != "myapp.example.com" {
t.Errorf("unexpected hostname: %q", resp.Hostname)
}
// Confirm exactly one underlying HTTP request was intercepted
if mock.CallCount() != 1 {
t.Errorf("expected 1 call, got %d", mock.CallCount())
}
}
```
--------------------------------
### Error Handling with Specific Error Types
Source: https://github.com/tordrt/go-turnstile/blob/main/README.md
Demonstrates how to check for specific error types returned by the verification process, such as timeouts, duplicate submissions, or invalid tokens.
```go
response, err := turnstileClient.VerifyToken(r.Context(), token, clientIP)
if err != nil {
// Check for specific error types
var timeoutErr turnstile.ErrTimeoutOrDuplicate
if errors.As(err, &timeoutErr) {
log.Println("Token timeout or duplicate submission:", err)
return
}
var invalidTokenErr turnstile.ErrInvalidInputResponse
if errors.As(err, &invalidTokenErr) {
log.Println("Invalid token provided:", err)
return
}
// Handle other verification errors
log.Println("Verification failed:", err)
return
}
```
--------------------------------
### Test Client - Duplicate Token Testing
Source: https://github.com/tordrt/go-turnstile/blob/main/README.md
Creates a test client that simulates a "token already spent" error. Use this to test your application's replay attack protection mechanisms.
```go
import "errors"
func TestHandlerDuplicateToken(t *testing.T) {
client := turnstile.NewTestClientTokenSpent()
_, err := client.VerifyToken(context.Background(), turnstile.TestToken)
var timeoutErr turnstile.ErrTimeoutOrDuplicate
if !errors.As(err, &timeoutErr) {
t.Fatal("Expected timeout-or-duplicate error")
}
// Test your duplicate submission handling
}
```
--------------------------------
### Advanced Client Configuration
Source: https://github.com/tordrt/go-turnstile/blob/main/README.md
Configures the Turnstile client with custom settings for retries, retry delay, and an HTTP client with a specific timeout.
```go
turnstileClient, err := turnstile.New(
"your-site-key",
"your-secret-key",
turnstile.WithMaxRetries(3),
turnstile.WithRetryDelay(200*time.Millisecond),
turnstile.WithHTTPClient(&http.Client{
Timeout: 5 * time.Second,
Transport: &http.Transport{
MaxIdleConns: 10,
},
}),
)
```
--------------------------------
### Test Constants for go-turnstile
Source: https://context7.com/tordrt/go-turnstile/llms.txt
Provides exported dummy keys and tokens for use with NewTestClient* helpers. These are derived from Cloudflare's official testing reference.
```go
package myhandler_test
import (
"fmt"
"github.com/tordrt/go-turnstile"
)
func printTestConstants() {
// Tokens / keys for use with NewTestClient* helpers
fmt.Println(turnstile.TestToken) // "XXXX.DUMMY.TOKEN.XXXX"
// Site keys (visible widget)
fmt.Println(turnstile.TestSiteKeyAlwaysPass) // "1x00000000000000000000AA"
fmt.Println(turnstile.TestSiteKeyAlwaysBlock) // "2x00000000000000000000AB"
// Site keys (invisible widget)
fmt.Println(turnstile.TestSiteKeyAlwaysPassInvisible) // "1x00000000000000000000BB"
fmt.Println(turnstile.TestSiteKeyAlwaysBlockInvisible) // "2x00000000000000000000BB"
// Site key that forces interactive challenge
fmt.Println(turnstile.TestSiteKeyForceChallenge) // "3x00000000000000000000FF"
// Secret keys that control server-side outcome
fmt.Println(turnstile.TestSecretKeyAlwaysPass) // "1x0000000000000000000000000000000AA"
fmt.Println(turnstile.TestSecretKeyAlwaysFail) // "2x0000000000000000000000000000000AA"
fmt.Println(turnstile.TestSecretKeyTokenSpent) // "3x0000000000000000000000000000000AA"
}
```
--------------------------------
### Test Client - Success Case Testing
Source: https://github.com/tordrt/go-turnstile/blob/main/README.md
Creates a test client that always passes verification. Use this to test the happy path logic of your application's integration with Turnstile.
```go
func TestHandlerSuccess(t *testing.T) {
client := turnstile.NewTestClient()
response, err := client.VerifyToken(context.Background(), turnstile.TestToken)
// err will be nil, response.Success will be true
}
```
--------------------------------
### Basic Token Verification
Source: https://github.com/tordrt/go-turnstile/blob/main/README.md
Verifies a given client token and IP address directly using the Turnstile client.
```go
// Verify a token directly
response, err := turnstileClient.VerifyToken(context.TODO(), clientToken, clientIP)
if err != nil {
// Handle verification error
return
}
// Verification successful
```
--------------------------------
### Client Options
Source: https://context7.com/tordrt/go-turnstile/llms.txt
Functional options passed to `New` to customize transport, endpoint, and retry behavior.
```APIDOC
## Client Options
### Description
Functional options used with the `turnstile.New` function to customize the client's behavior, including the HTTP client, verification endpoint, and retry strategy.
### Options
- **`WithHTTPClient(*http.Client)`**: Allows specifying a custom `http.Client` with custom configurations like timeouts and transports.
- **`WithVerifyEndpoint(string)`**: Overrides the default verification endpoint URL, useful for staging or enterprise proxies.
- **`WithMaxRetries(int)`**: Sets the maximum number of retries for transient network failures.
- **`WithRetryDelay(time.Duration)`**: Configures the initial delay for retries, which is then doubled exponentially for subsequent attempts.
### Usage Example
```go
client, err := turnstile.New(
"0x4AAAAAAA_sitekey",
"0x4AAAAAAA_secretkey",
turnstile.WithHTTPClient(&http.Client{Timeout: 10 * time.Second}),
turnstile.WithVerifyEndpoint("https://custom.endpoint.com/verify"),
turnstile.WithMaxRetries(5),
turnstile.WithRetryDelay(50*time.Millisecond),
)
```
```
--------------------------------
### Test Custom Mock Response in Go
Source: https://github.com/tordrt/go-turnstile/blob/main/README.md
Use `NewMockClientWithResponse` to provide a custom `MockResponse` for testing specific success scenarios. Ensure the `Action` field matches expected outcomes.
```go
func TestCustomScenario(t *testing.T) {
client, _ := turnstile.NewMockClientWithResponse(turnstile.MockResponse{
Success: true,
ChallengeTS: "2024-01-01T00:00:00Z",
Hostname: "myapp.example.com",
Action: "login",
})
response, _ := client.VerifyToken(context.Background(), "token")
if response.Action != "login" {
t.Errorf("Expected action 'login', got %q", response.Action)
}
}
```
--------------------------------
### Create HTTP Requests for Turnstile Testing
Source: https://context7.com/tordrt/go-turnstile/llms.txt
Helper functions to create `*http.Request` objects with the Turnstile test token. `NewTestRequest` builds a new request, while `AddTestToken` modifies an existing one.
```go
package myhandler_test
import (
"context"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"
"github.com/tordrt/go-turnstile"
)
func TestVerifyRequest_NewTestRequest(t *testing.T) {
client := turnstile.NewTestClient()
// Creates POST / with cf-turnstile-response + extra fields, RemoteAddr=192.0.2.1:54321
req := turnstile.NewTestRequest(map[string]string{
"username": "alice",
"email": "alice@example.com",
})
resp, err := client.VerifyRequest(context.Background(), req)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !resp.Success {
t.Error("expected success")
}
}
```
```go
func TestVerifyRequest_AddTestToken(t *testing.T) {
client := turnstile.NewTestClient()
// Pre-existing request from your test framework
form := url.Values{}
form.Set("username", "bob")
req := httptest.NewRequest(http.MethodPost, "/submit", strings.NewReader(form.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
if err := turnstile.AddTestToken(req); err != nil {
t.Fatalf("AddTestToken failed: %v", err)
}
resp, err := client.VerifyRequest(context.Background(), req)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !resp.Success {
t.Error("expected success")
}
}
```
--------------------------------
### Default Configuration Values
Source: https://github.com/tordrt/go-turnstile/blob/main/README.md
Lists the default values used by the go-turnstile library for verification endpoint, timeout, maximum retries, and retry delay.
```go
const (
DefaultVerifyEndpoint = "https://challenges.cloudflare.com/turnstile/v0/siteverify"
DefaultTimeout = 3 * time.Second
DefaultMaxRetries = 2
DefaultRetryDelay = 100 * time.Millisecond
)
```
--------------------------------
### MockRoundTripper.SetResponse - Dynamic Mock Behavior
Source: https://context7.com/tordrt/go-turnstile/llms.txt
Allows changing the mock response at runtime between calls. This enables stateful test scenarios, such as a first-call-succeeds / second-call-fails pattern.
```go
package myhandler_test
import (
"context"
"testing"
"github.com/tordrt/go-turnstile"
)
func TestMock_DynamicBehavior(t *testing.T) {
client, mock := turnstile.NewMockClient()
// First call: success
resp, err := client.VerifyToken(context.Background(), "token-1")
if err != nil || !resp.Success {
t.Fatal("expected first call to succeed")
}
// Switch to failure mode
mock.SetResponse(turnstile.MockResponse{
Success: false,
ErrorCodes: []string{"timeout-or-duplicate"},
})
// Second call: fails with ErrTimeoutOrDuplicate
_, err = client.VerifyToken(context.Background(), "token-2")
if err == nil {
t.Fatal("expected second call to fail")
}
if mock.CallCount() != 2 {
t.Errorf("expected 2 calls total, got %d", mock.CallCount())
}
}
```
--------------------------------
### Test HTTP Server Error Simulation in Go
Source: https://github.com/tordrt/go-turnstile/blob/main/README.md
Simulates HTTP errors by using `NewMockClientWithHTTPError`. This is useful for testing how the application handles non-2xx responses from the Turnstile service.
```go
func TestServerError(t *testing.T) {
client, _ := turnstile.NewMockClientWithHTTPError(500, "500 Internal Server Error")
_, err := client.VerifyToken(context.Background(), "token")
if err == nil {
t.Fatal("Expected error for 500 response")
}
}
```
--------------------------------
### Test Client - Failure Case Testing
Source: https://github.com/tordrt/go-turnstile/blob/main/README.md
Creates a test client that always fails verification. This is useful for testing how your application handles Turnstile verification errors.
```go
func TestHandlerFailure(t *testing.T) {
client := turnstile.NewTestClientAlwaysFail()
_, err := client.VerifyToken(context.Background(), turnstile.TestToken)
// err will not be nil - test your error handling here
if err == nil {
t.Fatal("Expected verification to fail")
}
}
```
--------------------------------
### NewMockClientWithHTTPError - Simulate HTTP-level Failures
Source: https://context7.com/tordrt/go-turnstile/llms.txt
Returns a mock that responds with a specified HTTP status code (e.g., 429, 500) instead of a JSON body. This enables testing of HTTP-layer error handling.
```go
package myhandler_test
import (
"context"
"strings"
"testing"
"github.com/tordrt/go-turnstile"
)
func TestMock_HTTPErrors(t *testing.T) {
tests := []struct {
code int
status string
}{
{500, "500 Internal Server Error"},
{429, "429 Too Many Requests"},
{503, "503 Service Unavailable"},
}
for _, tc := range tests {
t.Run(tc.status, func(t *testing.T) {
client, mock := turnstile.NewMockClientWithHTTPError(tc.code, tc.status)
_, err := client.VerifyToken(context.Background(), "token")
if err == nil {
t.Fatalf("expected error for HTTP %d, got nil", tc.code)
}
if !strings.Contains(err.Error(), string(rune('0'+tc.code/100))) {
t.Errorf("error message should reference status code, got: %v", err)
}
if mock.CallCount() != 1 {
t.Errorf("expected 1 call, got %d", mock.CallCount())
}
})
}
}
```
--------------------------------
### Verify Token from HTTP Request
Source: https://context7.com/tordrt/go-turnstile/llms.txt
Integrate Turnstile verification directly into an HTTP handler by extracting the token from the request and verifying it. Handles potential duplicate token errors.
```go
package main
import (
"errors"
"fmt"
"log"
"net/http"
"github.com/tordrt/go-turnstile"
)
func main() {
client, err := turnstile.New("0x4AAAAAAA_sitekey", "0x4AAAAAAA_secretkey")
if err != nil {
log.Fatal(err)
}
http.HandleFunc("/submit", func(w http.ResponseWriter, r *http.Request) {
response, err := client.VerifyRequest(r.Context(), r)
if err != nil {
var dupErr *turnstile.ErrTimeoutOrDuplicate
if errors.As(err, &dupErr) {
http.Error(w, "Token already used — please refresh and try again.", http.StatusBadRequest)
return
}
http.Error(w, "CAPTCHA verification failed.", http.StatusBadRequest)
return
}
// response.Success is guaranteed true here
fmt.Fprintf(w, "Welcome! Verified at %s from %s", response.ChallengeTS, response.Hostname)
})
log.Fatal(http.ListenAndServe(":8080", nil))
}
```
--------------------------------
### Create New Test HTTP Request with Turnstile Token
Source: https://github.com/tordrt/go-turnstile/blob/main/README.md
Use `NewTestRequest` to create an HTTP request pre-populated with a Turnstile test token. This is useful for testing handlers that expect a Turnstile token and potentially other form data.
```go
func TestHTTPHandler(t *testing.T) {
client := turnstile.NewTestClient()
// Create a test request with the Turnstile token and additional form data
req := turnstile.NewTestRequest(map[string]string{
"username": "testuser",
"email": "test@example.com",
})
// Verify using VerifyRequest - the test token is already in the request
response, err := client.VerifyRequest(context.Background(), req)
if err != nil {
t.Fatalf("Expected successful verification: %v", err)
}
// Now test your handler logic
// ...
}
```
```go
req := turnstile.NewTestRequest() // Just includes the Turnstile token
```
--------------------------------
### Client.VerifyRequest - Verify a token from an HTTP request
Source: https://context7.com/tordrt/go-turnstile/llms.txt
Extracts the `cf-turnstile-response` form field from the incoming request, strips the port from `RemoteAddr` to obtain the client IP, and calls `VerifyToken` automatically. This is the recommended method for standard form-based flows.
```APIDOC
## Client.VerifyRequest
Verifies a Turnstile token extracted from an HTTP request.
This method automatically extracts the `cf-turnstile-response` form field, determines the client's IP address, and calls the `VerifyToken` method. It is the recommended approach for typical web application flows where the token is submitted as part of an HTTP form.
### Method Signature
```go
func (c *Client) VerifyRequest(ctx context.Context, r *http.Request) (*VerifyResponse, error)
```
### Parameters
- **ctx** (context.Context) - Required - The context for the request, used for cancellation and deadlines.
- **r** (*http.Request) - Required - The incoming HTTP request object containing the Turnstile token.
### Returns
- **(*VerifyResponse, error)** - A `VerifyResponse` struct containing the verification result if successful, or an error if verification fails. Common errors include `ErrTimeoutOrDuplicate` for replay attacks or general verification failures.
```
--------------------------------
### Turnstile Response Structure in Go
Source: https://github.com/tordrt/go-turnstile/blob/main/README.md
Defines the structure for Turnstile verification responses in Go. Includes fields for success status, challenge timestamp, hostname, error codes, action, custom data, and metadata for enterprise users.
```go
type Response struct {
Success bool `json:"success"`
ChallengeTS string `json:"challenge_ts,omitempty"`
Hostname string `json:"hostname,omitempty"`
ErrorCodes []string `json:"error-codes,omitempty"`
Action string `json:"action,omitempty"`
CData string `json:"cdata,omitempty"`
Metadata *ResponseMetadata `json:"metadata,omitempty"` // Enterprise only
}
```
--------------------------------
### Client.VerifyToken
Source: https://context7.com/tordrt/go-turnstile/llms.txt
Verifies a raw Turnstile token string. Accepts an optional remote IP for additional validation. Automatically generates a UUID idempotency key per request and retries on transient network failures using exponential backoff.
```APIDOC
## Client.VerifyToken
### Description
Verifies a raw Turnstile token string. Accepts an optional remote IP for additional validation. Automatically generates a UUID idempotency key per request and retries on transient network failures using exponential backoff.
### Method
POST (implied by verification process, though not explicitly stated as HTTP)
### Endpoint
`/siteverify` (default, can be overridden)
### Parameters
#### Path Parameters
None
#### Query Parameters
None
#### Request Body
None (parameters are passed directly to the method)
### Request Example
```go
// Example using the go-turnstile library
response, err := client.VerifyToken(context.Background(), "raw_token_string", "client_ip_address")
```
### Response
#### Success Response (200)
- **Success** (bool) - Indicates if the token verification was successful.
- **ChallengeTS** (string) - The timestamp of the challenge in ISO 8601 format.
- **Hostname** (string) - The hostname of the site where the token was generated.
- **Action** (string) - The action name configured in the Cloudflare dashboard.
- **CData** (string) - Customer data passed to the widget.
- **ErrorCodes** ([]string) - A list of error codes if `Success` is false.
- **Metadata** (object) - Enterprise-only metadata, including `EphemeralID`.
#### Response Example
```json
{
"success": true,
"challenge_ts": "2024-06-15T12:00:00Z",
"hostname": "myapp.example.com",
"action": "submit_form",
"cdata": "user_session_123",
"error_codes": []
}
```
### Error Handling
Handles specific errors like `ErrEmptyToken`, `ErrInvalidInputResponse`, `ErrTimeoutOrDuplicate`, `ErrMissingInputSecret`, `ErrInvalidInputSecret`, and `ErrInternalError`.
```
--------------------------------
### HTML Front-end Integration with Cloudflare Turnstile
Source: https://context7.com/tordrt/go-turnstile/llms.txt
Integrates the Cloudflare Turnstile widget into an HTML form. The widget automatically injects a 'cf-turnstile-response' field upon completion, which is read by the backend verification process.
```html
Protected Form
```
--------------------------------
### Add Turnstile Test Token to Existing Request
Source: https://github.com/tordrt/go-turnstile/blob/main/README.md
Use `AddTestToken` to append a Turnstile test token to an existing `*http.Request` object. This is helpful when you have pre-constructed requests in your tests.
```go
func TestHTTPHandler(t *testing.T) {
client := turnstile.NewTestClient()
// You already have a request from your test setup
form := url.Values{}
form.Set("username", "testuser")
req := httptest.NewRequest(http.MethodPost, "/submit", strings.NewReader(form.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
// Add the test token to the existing request
err := turnstile.AddTestToken(req)
if err != nil {
t.Fatalf("Failed to add test token: %v", err)
}
// Verify the request
response, err := client.VerifyRequest(context.Background(), req)
if err != nil {
t.Fatalf("Expected successful verification: %v", err)
}
// Test your handler logic...
}
```
--------------------------------
### HTTP Request Verification
Source: https://github.com/tordrt/go-turnstile/blob/main/README.md
Verifies a Turnstile token directly from an incoming HTTP request. The token is automatically extracted from the 'cf-turnstile-response' form field, and the client IP is determined.
```go
func handlerFunction(w http.ResponseWriter, r *http.Request) {
response, err := turnstileClient.VerifyRequest(r.Context(), r)
if err != nil {
// Handle verification failure
return
}
// Verification successful
}
```
--------------------------------
### Verify Turnstile Token with IP Validation
Source: https://context7.com/tordrt/go-turnstile/llms.txt
Verifies a raw Turnstile token string, optionally including the client's IP address for enhanced validation. Handles various specific error types returned by the Turnstile API.
```go
package main
import (
"context"
"errors"
"fmt"
"log"
"github.com/tordrt/go-turnstile"
)
func verifyFormToken(client *turnstile.Client, token, clientIP string) {
// With IP validation
response, err := client.VerifyToken(context.Background(), token, clientIP)
if err != nil {
switch {
case errors.As(err, &turnstile.ErrEmptyToken):
fmt.Println("Token was empty")
case errors.As(err, &turnstile.ErrInvalidInputResponse{}):
fmt.Println("Token is invalid or expired")
case errors.As(err, &turnstile.ErrTimeoutOrDuplicate{}):
fmt.Println("Token timed out or was already used (replay attack)")
case errors.As(err, &turnstile.ErrMissingInputSecret{}):
fmt.Println("Server misconfiguration: secret key missing")
case errors.As(err, &turnstile.ErrInvalidInputSecret{}):
fmt.Println("Server misconfiguration: secret key is invalid")
case errors.As(err, &turnstile.ErrInternalError{}):
fmt.Println("Cloudflare internal error — safe to retry")
default:
log.Printf("Unexpected error: %v", err)
}
return
}
// Access full response metadata
fmt.Printf("Success=%v TS=%s Hostname=%s Action=%s CData=%s\n",
response.Success, response.ChallengeTS, response.Hostname, response.Action, response.CData)
}
```
=== COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.