Skip to content

Commit 9b106db

Browse files
authored
add Cloudflare Turnstile example (vercel#41283)
## Description close vercel#41110 ## Documentation / Examples - [x] Make sure the linting passes by running `pnpm lint` - [x] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
1 parent 6652d78 commit 9b106db

File tree

11 files changed

+266
-0
lines changed

11 files changed

+266
-0
lines changed

Diff for: examples/cloudflare-turnstile/.env.local.example

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Public Environment variables that can be used in the browser.
2+
NEXT_PUBLIC_CLOUDFLARE_TURNSTILE_SITE_KEY=
3+
4+
# Secret environment variables only available to Node.js
5+
CLOUDFLARE_TURNSTILE_SECRET_KEY=

Diff for: examples/cloudflare-turnstile/.gitignore

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
8+
# testing
9+
/coverage
10+
11+
# next.js
12+
/.next/
13+
/out/
14+
15+
# production
16+
/build
17+
18+
# misc
19+
.DS_Store
20+
*.pem
21+
22+
# debug
23+
npm-debug.log*
24+
yarn-debug.log*
25+
yarn-error.log*
26+
.pnpm-debug.log*
27+
28+
# local env files
29+
.env*.local
30+
31+
# vercel
32+
.vercel
33+
34+
# typescript
35+
*.tsbuildinfo
36+
next-env.d.ts

Diff for: examples/cloudflare-turnstile/README.md

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Example with Cloudflare Turnstile
2+
3+
[Turnstile](https://developers.cloudflare.com/turnstile/) is Cloudflare’s smart CAPTCHA alternative. It can be embedded into any website without sending traffic through Cloudflare and works without showing visitors a CAPTCHA.
4+
5+
This example shows how you can use **Cloudflare Turnstile** with your Next.js project. You can see a [live version here](https://with-cloudflare-turnstile.vercel.app/).
6+
7+
## Deploy your own
8+
9+
Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example).
10+
11+
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/cloudflare-turnstile&project-name=cloudflare-turnstile&repository-name=cloudflare-turnstile)
12+
13+
**Important**: When you import your project on Vercel, make sure to click on **Environment Variable**s and set them to match your .env.local file.
14+
15+
## How to use
16+
17+
Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init), [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/), or [pnpm](https://pnpm.io) to bootstrap the example:
18+
19+
```bash
20+
npx create-next-app --example cloudflare-turnstile cloudflare-turnstile-app
21+
```
22+
23+
```bash
24+
yarn create next-app --example cloudflare-turnstile cloudflare-turnstile-app
25+
```
26+
27+
```bash
28+
pnpm create next-app --example cloudflare-turnstile cloudflare-turnstile-app
29+
```
30+
31+
Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).
32+
33+
## Configuring Cloudflare Turnstile
34+
35+
### Get a sitekey and secret key
36+
37+
1. Go to the [Cloudflare dashboard](https://dash.cloudflare.com/?to=/:account/turnstile) and select your account.
38+
2. Go to Turnstile.
39+
3. Select Add a site and fill out the form.
40+
4. Copy your **Site Key** and **Secret Key**.
41+
42+
### Set up environment variables
43+
44+
To connect the app with Cloudflare Turnstile, you'll need to add the settings from your Cloudflare dashboard as environment variables
45+
46+
Copy the .env.local.example file in this directory to .env.local.
47+
48+
```bash
49+
cp .env.local.example .env.local
50+
```
51+
52+
Then, open .env.local and fill these environment variables:
53+
54+
- `NEXT_PUBLIC_CLOUDFLARE_TURNSTILE_SITE_KEY`
55+
- `CLOUDFLARE_TURNSTILE_SECRET_KEY`

Diff for: examples/cloudflare-turnstile/app.css

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
html,
2+
body {
3+
display: grid;
4+
place-content: center;
5+
margin: 0;
6+
height: 100%;
7+
}
8+
9+
main {
10+
height: 50vh;
11+
width: 300px;
12+
text-align: center;
13+
}
14+
15+
button[type='submit'] {
16+
cursor: pointer;
17+
width: 100%;
18+
outline: none;
19+
border: none;
20+
padding: 0.5rem 1rem;
21+
margin-top: 1rem;
22+
border-radius: 0.25rem;
23+
background: #0d6efd;
24+
font-size: 1.25rem;
25+
color: #ffffff;
26+
}
27+
28+
.checkbox {
29+
min-height: 70px;
30+
}

Diff for: examples/cloudflare-turnstile/next.config.js

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
module.exports = {
2+
async rewrites() {
3+
return [
4+
{
5+
source: '/',
6+
destination: '/implicit',
7+
},
8+
]
9+
},
10+
}

Diff for: examples/cloudflare-turnstile/package.json

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"private": true,
3+
"scripts": {
4+
"dev": "next",
5+
"build": "next build",
6+
"start": "next start"
7+
},
8+
"dependencies": {
9+
"next": "latest",
10+
"react": "^18.2.0",
11+
"react-dom": "^18.2.0"
12+
},
13+
"devDependencies": {
14+
"@types/node": "^18.0.0",
15+
"@types/react": "^18.0.14",
16+
"@types/react-dom": "^18.0.5",
17+
"typescript": "^4.7.4"
18+
}
19+
}

Diff for: examples/cloudflare-turnstile/pages/_app.tsx

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import type { AppProps } from 'next/app'
2+
import '../app.css'
3+
4+
export default function App({ Component, pageProps }: AppProps) {
5+
return <Component {...pageProps} />
6+
}

Diff for: examples/cloudflare-turnstile/pages/api/handler.ts

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import type { NextApiRequest, NextApiResponse } from 'next'
2+
3+
export default async function Handler(
4+
req: NextApiRequest,
5+
res: NextApiResponse
6+
) {
7+
const form = new URLSearchParams()
8+
form.append('secret', process.env.CLOUDFLARE_TURNSTILE_SECRET_KEY)
9+
form.append('response', req.body['cf-turnstile-response'])
10+
form.append('remoteip', req.headers['x-forwarded-for'] as string)
11+
12+
const result = await fetch(
13+
'https://challenges.cloudflare.com/turnstile/v0/siteverify',
14+
{ method: 'POST', body: form }
15+
)
16+
const json = await result.json()
17+
res.status(result.status).json(json)
18+
}

Diff for: examples/cloudflare-turnstile/pages/explicit.tsx

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import Script from 'next/script'
2+
3+
type RenderParameters = {
4+
sitekey: string
5+
theme?: 'light' | 'dark'
6+
callback?(token: string): void
7+
}
8+
9+
declare global {
10+
interface Window {
11+
onloadTurnstileCallback(): void
12+
turnstile: {
13+
render(container: string | HTMLElement, params: RenderParameters): void
14+
}
15+
}
16+
}
17+
18+
export default function ExplicitRender() {
19+
return (
20+
<main>
21+
<Script id="cf-turnstile-callback">
22+
{`window.onloadTurnstileCallback = function () {
23+
window.turnstile.render('#my-widget', {
24+
sitekey: '${process.env.NEXT_PUBLIC_CLOUDFLARE_TURNSTILE_SITE_KEY}',
25+
})
26+
}`}
27+
</Script>
28+
<Script
29+
src="https://challenges.cloudflare.com/turnstile/v0/api.js?onload=onloadTurnstileCallback"
30+
async={true}
31+
defer={true}
32+
/>
33+
<form method="POST" action="/api/handler">
34+
<h2>Dummy Login Demo</h2>
35+
<div id="my-widget" className="checkbox" />
36+
<button type="submit">Sign in</button>
37+
<p>
38+
Go to the <a href="/implicit">implicit render demo</a>
39+
</p>
40+
</form>
41+
</main>
42+
)
43+
}

Diff for: examples/cloudflare-turnstile/pages/implicit.tsx

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import Script from 'next/script'
2+
3+
export default function ImplicitRender() {
4+
return (
5+
<main>
6+
<Script
7+
src="https://challenges.cloudflare.com/turnstile/v0/api.js"
8+
async={true}
9+
defer={true}
10+
/>
11+
<form method="POST" action="/api/handler">
12+
<h2>Dummy Login Demo</h2>
13+
<div
14+
className="cf-turnstile checkbox"
15+
data-sitekey={process.env.NEXT_PUBLIC_CLOUDFLARE_TURNSTILE_SITE_KEY}
16+
/>
17+
<button type="submit">Sign in</button>
18+
<p>
19+
Go to the <a href="/explicit">explicit render demo</a>
20+
</p>
21+
</form>
22+
</main>
23+
)
24+
}

Diff for: examples/cloudflare-turnstile/tsconfig.json

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"compilerOptions": {
3+
"target": "es5",
4+
"lib": ["dom", "dom.iterable", "esnext"],
5+
"allowJs": true,
6+
"skipLibCheck": true,
7+
"strict": false,
8+
"forceConsistentCasingInFileNames": true,
9+
"noEmit": true,
10+
"esModuleInterop": true,
11+
"module": "esnext",
12+
"moduleResolution": "node",
13+
"resolveJsonModule": true,
14+
"isolatedModules": true,
15+
"jsx": "preserve",
16+
"incremental": true
17+
},
18+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
19+
"exclude": ["node_modules"]
20+
}

0 commit comments

Comments
 (0)