Giter Site home page Giter Site logo

chez14 / react-turnstile Goto Github PK

View Code? Open in Web Editor NEW

This project forked from marsidev/react-turnstile

0.0 0.0 0.0 392 KB

Cloudflare Turnstile integration for React.

Home Page: https://www.npmjs.com/package/@marsidev/react-turnstile

License: MIT License

JavaScript 2.18% TypeScript 97.46% CSS 0.35%

react-turnstile's Introduction

React Turnstile

Cloudflare Turnstile integration for React.


npm version npm downloads install size bundle size CI status tests missing PRs are welcome

Features

  • 💪 smart verification with minimal user interaction
  • 🕵️‍♀️ privacy-focused approach
  • 💉 automatic script injection
  • ⚡️ ssr ready

Demo

https://react-turnstile.vercel.app/

Install

  1. Follow these steps to obtain a free site key and secret key from Cloudflare.

  2. Install @marsidev/react-turnstile into your React project.

    npm i @marsidev/react-turnstile

    Or using any custom package manager:

    pnpm add @marsidev/react-turnstile
    yarn add @marsidev/react-turnstile
    bun add @marsidev/react-turnstile
    ni @marsidev/react-turnstile

Usage

The only required prop is the siteKey.

import { Turnstile } from '@marsidev/react-turnstile'

function Widget() {
  return <Turnstile siteKey='1x00000000000000000000AA' />
}

Props

Prop Type Description Required
siteKey string Your sitekey key, get one from here.
options object Widget render options. More info about this options below.
scriptOptions object You can customize the injected script tag with this prop. It allows you to add async, defer, nonce attributes to the script tag. You can also control whether the injected script will be added to the document body or head with appendTo attribute.
onSuccess function Callback that is invoked upon success of the challenge. The callback is passed a token that can be validated.
onExpire function Callback that is invoked when a challenge expires.
onError function Callback that is invoked when there is a network error.
autoResetOnExpire boolean Controls whether the widget should automatically reset when it expires. If is set to true, you don't need to use the onExpire callback. Default to true.

Render options

Option Type Default Description
theme string 'auto' The widget theme. You can choose between light, dark or auto.
tabIndex number 0 The tabindex of Turnstile’s iframe for accessibility purposes.
action string undefined A customer value that can be used to differentiate widgets under the same sitekey in analytics and which is returned upon validation. This can only contain up to 32 alphanumeric characters including _ and -.
cData string undefined A customer payload that can be used to attach customer data to the challenge throughout its issuance and which is returned upon validation. This can only contain up to 255 alphanumeric characters including _ and -.
responseField boolean true A boolean that controls if an input element with the response token is created.
responseFieldName string 'cf-turnstile-response' Name of the input element.
size string 'normal' The widget size. Can take the following values: 'normal', 'compact'. The normal size is 300x65px, the compact size is 130x120px.
retry string 'auto' Controls whether the widget should automatically retry to obtain a token if it did not succeed. The default is 'auto', which will retry automatically. This can be set to 'never' to disable retry upon failure.
retryInterval number 8000 When retry is set to 'auto', retryInterval controls the time between retry attempts in milliseconds. The value must be a positive integer less than 900000. When retry is set to 'never', this parameter has no effect.

All this options are optional.

Read the docs to get more info about this options.

The widget is wrapped in a div, so you can pass any valid div prop such as className, id, or style.

Script options

Option Type Default Description
nonce string undefined Custom nonce for the injected script.
defer boolean true Define if set the injected script as defer.
async boolean true Define if set the injected script as async.
appendTo string 'head' Define if inject the script in the head or in the body.
id string 'cf-turnstile-script' Custom ID of the injected script.
onLoadCallbackName string 'onloadTurnstileCallback' Custom name of the onload callback.

Examples

Rendering the widget:

import { Turnstile } from '@marsidev/react-turnstile'

function Widget() {
  return <Turnstile siteKey='1x00000000000000000000AA' />
}

Rendering the widget with custom props:

import { Turnstile } from '@marsidev/react-turnstile'

function Widget() {
  return (
    <Turnstile
      siteKey='1x00000000000000000000AA'
      className='fixed bottom-4 right-4'
      options={{
        action: 'submit-form',
        theme: 'light',
        size: 'compact'
      }}
      scriptOptions={{
        appendTo: 'body'
      }}
    />
  )
}

Managing widget rendering status:

import { useState } from 'react'
import { Turnstile } from '@marsidev/react-turnstile'

function Widget() {
  const [status, setStatus] = useState()

  return (
    <Turnstile
      siteKey='1x00000000000000000000AA'
      onError={() => setStatus('error')}
      onExpire={() => setStatus('expired')}
      onSuccess={() => setStatus('solved')}
    />
  )
}

onExpire does not take effect unless you set autoResetOnExpire to false.

Getting the token after solving the challenge:

import { useState } from 'react'
import { Turnstile } from '@marsidev/react-turnstile'

function Widget() {
  const [token, setToken] = useState()

  return (
    <Turnstile
      siteKey='1x00000000000000000000AA'
      onSuccess={(token) => setToken(token)}
    />
  )
}

Interacting with the widget:

import { useRef } from 'react'
import { Turnstile } from '@marsidev/react-turnstile'

function Widget() {
  const ref = useRef(null)

  return (
    <>
      <Turnstile ref={ref} siteKey='1x00000000000000000000AA'/>

      <button onClick={() => alert(ref.current?.getResponse())}>
        Get response
      </button>

      <button onClick={() => ref.current?.reset()}>
        Reset widget
      </button>

      <button onClick={() => ref.current?.remove()}>
        Remove widget
      </button>

      <button onClick={() => ref.current?.render()}>
        Render widget
      </button>
    </>
  )
}

Interacting with the widget (using TypeScript):

import { useRef } from 'react'
import { Turnstile, type TurnstileInstance } from '@marsidev/react-turnstile'

function Widget() {
  const ref = useRef<TurnstileInstance>(null)

  return (
    <>
      <Turnstile ref={ref} siteKey='1x00000000000000000000AA'/>

      <button onClick={() => alert(ref.current?.getResponse())}>
        Get response
      </button>
    </>
  )
}

Validating a token:

// LoginForm.jsx
import { useRef } from 'react'
import { Turnstile } from '@marsidev/react-turnstile'

export default function LoginForm() {
  const formRef = useRef(null)

  async function handleSubmit(event) {
    event.preventDefault()
    const formData = new FormData(formRef.current)
    const token = formData.get('cf-turnstile-response')

    const res = await fetch('/api/verify', {
      method: 'POST',
      body: JSON.stringify({ token }),
      headers: {
        'content-type': 'application/json'
      }
    })

    const data = await res.json()
    if (data.success) {
      // the token has been validated
    }
  }

  return (
    <form ref={formRef} onSubmit={handleSubmit}>
      <input type="text" placeholder="username"/>
      <input type="password" placeholder="password"/>
      <Turnstile siteKey='1x00000000000000000000AA'/>
      <button type='submit'>Login</button>
    </form>
  )
}
// `pages/api/verify.js`
// this is an example of a next.js api route
// this code runs on the server
const endpoint = 'https://challenges.cloudflare.com/turnstile/v0/siteverify'
const secret = '1x0000000000000000000000000000000AA'

export default async function handler(request, response) {
  const body = `secret=${encodeURIComponent(secret)}&response=${encodeURIComponent(request.body.token)}`

  const res = await fetch(endpoint, {
    method: 'POST',
    body,
    headers: {
      'content-type': 'application/x-www-form-urlencoded'
    }
  })

  const data = await res.json()
  return response.json(data)
}

Check the demo and his source code to see a code similar to the above in action.

Check the docs for more info about server side validation.

As you might noted, there is three ways to get the token response from a solved challenge:

  • by catching it from the onSuccess callback.
  • by calling the .getResponse() method.
  • by reading the widget response input with name cf-turnstile-response. This one is not an option if you set options.fieldResponse to false.

Handling widget expiring:

By default, you don't need to handle the widget expiring, unless you set autoResetOnExpire to false.

import { useRef } from 'react'
import { Turnstile } from '@marsidev/react-turnstile'

function Widget() {
  const ref = useRef(null)

  return (
    <Turnstile
      ref={ref}
      autoResetOnExpire={false}
      siteKey='1x00000000000000000000AA'
      onExpire={() => ref.current?.reset()}
    />
  )
}

Contributing

Any contributions are greatly appreciated. If you have a suggestion that would make this project better, please fork the repo and create a Pull Request. You can also open an issue.

Development

  • Fork or clone this repository.
  • Install pnpm.
  • Install dependencies with pnpm install.
  • You can use pnpm dev to start the demo page in development mode, which also rebuild the library when file changes are detected in the src folder.
  • You also can use pnpm stub, which run unbuild --stub, a passive watcher to use the library while developing without needing to watch and rebuild. However, this option can't be used in an esm context.

Credits

Inspired by

License

Published under the MIT License.

react-turnstile's People

Contributors

marsidev avatar luca-rath avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.