PermaData Captcha

A user-friendly bot detection system that analyzes natural mouse movements

There are two ways to install PermaData Captcha: using the React component or the standalone script.

1. Install the package

npm install @permadata/captcha

2. Import and use the component

import { CaptchaCheckbox } from '@permadata/captcha';

function MyForm() {
  const [isCaptchaValid, setIsCaptchaValid] = useState(false);
  const [captchaToken, setCaptchaToken] = useState(null);
  
  const handleCaptchaValidityChange = (isValid, token) => {
    setIsCaptchaValid(isValid);
    setCaptchaToken(token);
  };
  
  return (
    <form>
      {/* Your form fields */}
      <CaptchaCheckbox onValidityChange={handleCaptchaValidityChange} />
      <button type="submit" disabled={!isCaptchaValid}>Submit</button>
    </form>
  );
}

Server-Side Verification

Link to this section

Always verify the captcha token on your server before processing form submissions. PermaData provides a server-side verification library for multiple platforms.

1. Install the package

npm install @permadata/captcha-verify

2. Verify the token

// Next.js API Route or Server Action
import { verifyCaptchaToken } from '@permadata/captcha-verify';

// In a Next.js API route
export async function POST(request) {
  const { captchaToken, ...formData } = await request.json();
  
  // Verify the captcha token
  const result = await verifyCaptchaToken(captchaToken, {
    apiKey: process.env.PERMADATA_API_KEY // Your PermaData API key
  });
  
  if (!result.valid) {
    return Response.json({ 
      success: false, 
      error: 'Captcha verification failed' 
    }, { status: 400 });
  }
  
  // Process the form data
  // ...
  
  return Response.json({ success: true });
}

// In a Server Action
'use server'

export async function processForm(formData) {
  const captchaToken = formData.get('captcha-token');
  
  // Verify the captcha token
  const result = await verifyCaptchaToken(captchaToken, {
    apiKey: process.env.PERMADATA_API_KEY // Your PermaData API key
  });
  
  if (!result.valid) {
    return { 
      success: false, 
      error: 'Captcha verification failed' 
    };
  }
  
  // Process the form data
  // ...
  
  return { success: true };
}

Complete Implementation Example

Link to this section

Here's a complete example of a contact form with client-side and server-side captcha validation using Next.js.

Client Component

// app/contact/page.tsx
'use client'

import { useState } from 'react'
import { CaptchaCheckbox } from '@permadata/captcha'
import { submitContactForm } from '@/app/actions/contact-form'

export default function ContactPage() {
  const [isCaptchaValid, setIsCaptchaValid] = useState(false)
  const [captchaToken, setCaptchaToken] = useState<string | undefined>(undefined)
  const [isSubmitting, setIsSubmitting] = useState(false)
  const [formResponse, setFormResponse] = useState<{ success: boolean; message?: string; error?: string } | null>(null)

  const handleCaptchaValidityChange = (isValid: boolean, token?: string) => {
    setIsCaptchaValid(isValid)
    setCaptchaToken(token)
  }

  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault()

    if (!isCaptchaValid || !captchaToken) {
      setFormResponse({
        success: false,
        error: "Please complete the captcha verification",
      })
      return
    }

    setIsSubmitting(true)
    setFormResponse(null)

    try {
      // Get form data
      const formData = new FormData(e.currentTarget)

      // Add captcha token to form data
      formData.append("captcha-token", captchaToken)

      // Submit the form
      const response = await submitContactForm(formData)
      setFormResponse(response)

      // Reset form on success
      if (response.success) {
        e.currentTarget.reset()
        setIsCaptchaValid(false)
        setCaptchaToken(undefined)
      }
    } catch (error) {
      setFormResponse({
        success: false,
        error: "An unexpected error occurred",
      })
    } finally {
      setIsSubmitting(false)
    }
  }

  return (
    <div className="max-w-md mx-auto">
      <h1 className="text-2xl font-bold mb-6">Contact Us</h1>
      
      {formResponse?.success ? (
        <div className="bg-green-50 p-6 rounded-md">
          <h3 className="text-xl font-semibold text-green-800 mb-2">Message Sent!</h3>
          <p className="text-green-700">{formResponse.message}</p>
        </div>
      ) : (
        <form onSubmit={handleSubmit} className="space-y-6">
          {formResponse?.error && (
            <div className="bg-red-50 p-4 rounded-md">
              <span className="text-red-700">{formResponse.error}</span>
            </div>
          )}

          <div>
            <label htmlFor="name" className="block text-sm font-medium text-gray-700">Name</label>
            <input 
              id="name" 
              name="name" 
              type="text" 
              required 
              className="mt-1 block w-full rounded-md border-gray-300 shadow-sm"
            />
          </div>

          <div>
            <label htmlFor="email" className="block text-sm font-medium text-gray-700">Email</label>
            <input 
              id="email" 
              name="email" 
              type="email" 
              required 
              className="mt-1 block w-full rounded-md border-gray-300 shadow-sm"
            />
          </div>

          <div>
            <label htmlFor="message" className="block text-sm font-medium text-gray-700">Message</label>
            <textarea 
              id="message" 
              name="message" 
              rows={4} 
              required 
              className="mt-1 block w-full rounded-md border-gray-300 shadow-sm"
            ></textarea>
          </div>

          <div>
            <CaptchaCheckbox onValidityChange={handleCaptchaValidityChange} />
          </div>

          <button
            type="submit"
            className="w-full bg-blue-600 text-white py-2 px-4 rounded-md"
            disabled={isSubmitting || !isCaptchaValid}
          >
            {isSubmitting ? "Sending..." : "Send Message"}
          </button>
        </form>
      )}
    </div>
  )
}

Server Action

// app/actions/contact-form.ts
'use server'

import { verifyCaptchaToken } from '@permadata/captcha-verify'

export async function submitContactForm(formData: FormData) {
  // Extract the captcha token
  const captchaToken = formData.get("captcha-token") as string

  if (!captchaToken) {
    return { success: false, error: "Captcha verification required" }
  }

  // Verify the captcha token
  try {
    const verifyResult = await verifyCaptchaToken(captchaToken, {
      apiKey: process.env.PERMADATA_API_KEY // Your PermaData API key
    })

    if (!verifyResult.valid) {
      return { success: false, error: verifyResult.error || "Captcha verification failed" }
    }

    // Extract form data
    const name = formData.get("name") as string
    const email = formData.get("email") as string
    const message = formData.get("message") as string

    // Validate form data
    if (!name || !email || !message) {
      return { success: false, error: "All fields are required" }
    }

    // Process the form data (e.g., send email, save to database)
    // This is just a placeholder for your actual implementation
    console.log("Form submission:", { name, email, message })

    // Return success response
    return {
      success: true,
      message: "Thank you for your message! We will get back to you soon.",
    }
  } catch (error) {
    console.error("Error processing form:", error)
    return { success: false, error: "An error occurred while processing your request" }
  }
}

CaptchaCheckbox Component Props

PropTypeDefaultDescription
onValidityChange(isValid: boolean, token?: string) => voidundefinedCallback function that receives the validation state and token
classNamestring""Additional CSS classes to apply to the component
theme"light" | "dark""light"Visual theme of the captcha component

Verification API Response

PropertyTypeDescription
validbooleanWhether the captcha token is valid
formTokenstringA new token for form submission (only present if valid is true)
errorstringError message (only present if valid is false)

Security Considerations

Link to this section

While PermaData Captcha provides strong protection against automated bots, it's important to follow these best practices:

  • Always validate on the server: Client-side validation can be bypassed, so always verify the captcha token on your server before processing sensitive operations.
  • Token expiration: Captcha tokens expire after 15 minutes. Ensure users complete forms within this timeframe or prompt them to refresh the captcha.
  • Additional protections: For highly sensitive operations, consider combining the captcha with other security measures like rate limiting and IP-based restrictions.
  • Keep your API keys secure: Never expose your PermaData API keys in client-side code.

Troubleshooting

Link to this section

Captcha not validating

If the captcha is not validating properly, ensure that:

  • You're correctly passing the token to your server
  • The token hasn't expired (tokens are valid for 15 minutes)
  • Your server can reach the verification API endpoint
  • You're using the correct API key

Accessibility issues

The captcha is designed to be accessible, but if users report issues:

  • Ensure keyboard navigation is working properly
  • Check that screen readers can interpret the captcha status
  • Consider providing an alternative verification method for users with disabilities

Integration issues

If you're having trouble integrating the captcha:

  • Check that you're using the latest version of the package
  • Ensure you've followed all installation steps correctly
  • Check the browser console for any JavaScript errors
  • Verify that your server environment meets all requirements

If you need help with PermaData Captcha, there are several resources available:

Documentation

Comprehensive documentation with examples and API references.

View Documentation

Community Forum

Connect with other developers and get help from the community.

Join the Community

Support

Get direct support from our team for technical issues.

Contact Support

GitHub

Report issues, contribute, or explore the source code.

View on GitHub