### 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.