Skip to content

Commit 058e295

Browse files
committed
update docs
1 parent 923c063 commit 058e295

File tree

1 file changed

+33
-144
lines changed

1 file changed

+33
-144
lines changed

README.md

Lines changed: 33 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ BitArmory.Turnstile for .NET and C#
55

66
Project Description
77
-------------------
8-
:recycle: A minimal, no-drama, friction-less **C#** **HTTP** verification client for **Cloudflare**'s [**Turnstile** API](https://developers.cloudflare.com/turnstile/).
8+
A minimal, no-drama, friction-less **C#** **HTTP** verification client for **Cloudflare**'s [**Turnstile** API](https://developers.cloudflare.com/turnstile/).
99

1010
The problem with current **Turnstile** libraries in **.NET** is that all of them take a hard dependency on the underlying web framework like **ASP.NET WebForms**, **ASP.NET MVC 5**, **ASP.NET Core**, or **ASP.NET Razor Pages**.
1111

@@ -39,7 +39,7 @@ Install-Package BitArmory.Turnstile
3939
General Usage
4040
-------------
4141
### Getting Started
42-
You'll need a **Cloudflare** account. You can sign up [here](https://www.cloudflare.com/)! After you sign up and setup your domain, you'll have two important pieces of information:
42+
You'll need a **Cloudflare** account. Get started by following the directions [here](https://developers.cloudflare.com/turnstile/get-started/)! After you sign up and you're signed into the dashboard; you'll need two important pieces of information:
4343
1. Your `site` key
4444
2. Your `secret` key
4545

@@ -52,202 +52,91 @@ This library supports all widget types:
5252
## Turnstile
5353
### Client-side Setup
5454

55-
Be sure to checkout [this video that describes how Turnstile v3 works](https://www.youtube.com/watch?v=tbvxFW4UJdU) before implementing.
55+
Be sure to [read the documentation](https://developers.cloudflare.com/turnstile/) before implementing.
5656

57-
Then, on every page of your website, add the following JavaScript:
57+
Then, to protect an **HTML** form submission on your website, add the following:
5858
```html
5959
<html>
6060
<head>
61-
<script src='https://www.google.com/Turnstile/api.js?render=GOOGLE_SITE_KEY'></script>
61+
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
6262
</head>
6363
<body>
64-
...
65-
<script>
66-
gTurnstile.ready(function() {
67-
gTurnstile.execute('GOOGLE_SITE_KEY', {action: 'TAG'});
68-
});
69-
</script>
70-
</body>
71-
</html>
72-
```
73-
Every page should call `gTurnstile.execute` with some unique **action** `TAG`. [Read more about actions in the official docs here](https://developers.google.com/Turnstile/docs/v3#actions).
74-
75-
When it is time to validate an **HTTP** `POST` you'll need transfer the captcha `token` in the browser to a hidden HTML form field as shown below:
76-
77-
```html
78-
<html>
79-
<body>
80-
<form action='/do-post' method='POST'>
81-
<input id="captcha" type="hidden" name="captcha" value="" />
82-
</form>
83-
<script>
84-
function ExecuteTurnstile_OnSome_ButtonAction(){
85-
gTurnstile.ready(function() {
86-
gTurnstile.execute('GOOGLE_SITE_KEY', {action: 'SomeAction'})
87-
.then(function(token) {
88-
// Set `token` in a hidden form input.
89-
$("#captcha").val(token);
90-
91-
//And finally submit the form by firing
92-
//off the HTTP POST over the wire at this
93-
//exact moment in time here.
94-
});
95-
});
96-
}
97-
</script>
98-
</body>
99-
</html>
100-
```
101-
You'll need to execute `ExecuteTurnstile_OnSome_ButtonAction()` function the moment the user decides to submit your form. Otherwise, if you run `gTurnstile.*` code during page load, the token being copied to the hidden field can expire after a few minutes. This means, if the user takes a long time filling out a form, the token copied at page load can expire and your server will validate an expired token by the time the form is submitted resulting in a failed captcha verification.
102-
103-
Therefore, you should execute the `ExecuteTurnstile_OnSome_ButtonAction()` function on some `onclick=` event to get a fresh token before the form is submitted.
104-
105-
Also, keep in mind, `gTurnstile.execute()` returns a **JavaScript Promise**. You won't have a valid token in your `<form>` until the line `$("#captcha").val(token);` above executes. So you'll need to postpone the form submission until `$("#captcha").val(token);` is actually executed. Then, *and only then,* you can continue submitting the HTML form to have it validated on your server with a valid token.
106-
107-
### Verifying the POST Server-side
108-
When the `POST` is received on the server:
109-
1. Get the client's IP address. If you're using **CloudFlare**, be sure to use the [`CF-Connecting-IP` header value][0].
110-
2. Extract the `#captcha` value (client token) in the hidden **HTML** form field.
111-
3. Use the `TurnstileService` to verify the client's **Turnstile** is valid.
112-
113-
```csharp
114-
//1. Get the client IP address in your chosen web framework
115-
string clientIp = GetClientIpAddress();
116-
string token = null;
117-
string secret = "your_secret_key";
118-
119-
//2. Extract the `#captcha` field from the hidden HTML form in your chosen web framework
120-
if( this.Request.Form.TryGetValue("captcha", out var formField) )
121-
{
122-
token = formField;
123-
}
124-
125-
//3. Validate the Turnstile with Google
126-
var captchaApi = new TurnstileService();
127-
var result = await captchaApi.Verify3Async(token, clientIp, secret);
128-
129-
if( !result.IsSuccess || result.Action != "SOME_ACTION" || result.Score < 0.5 )
130-
{
131-
// The POST is not valid
132-
return new BadRequestResult();
133-
}
134-
else{
135-
//continue processing, everything is okay!
136-
}
137-
```
138-
139-
<details><summary>GetClientIpAddress() in ASP.NET Core</summary>
140-
<p>
141-
142-
**Note:** If your site is behind CloudFlare, be sure you're suing the [`CF-Connecting-IP` header value][0] instead.
143-
144-
```csharp
145-
public string GetClientIpAddress(){
146-
return this.HttpContext.Connection.RemoteIpAddress.ToString();
147-
}
148-
```
149-
150-
</p>
151-
</details>
152-
153-
<details><summary>GetClientIpAddress() in ASP.NET WebForms</summary>
154-
<p>
155-
156-
**Note:** If your site is behind CloudFlare, be sure you're suing the [`CF-Connecting-IP` header value][0] instead.
157-
158-
```csharp
159-
public string GetClientIpAddress(){
160-
return this.Request.UserHostAddress;
161-
}
162-
```
163-
164-
</p>
165-
</details>
166-
167-
You'll want to make sure the action name you choose for the request is legitimate. The `result.Score` is the probably of a human. So, you'll want to make sure you have a `result.Score > 0.5`; anything less is probably a bot.
168-
169-
## Turnstile v2 (I'm not a robot)
170-
### Client-side Setup
171-
Add the following `<div class="g-Turnstile">` and `<script>` tags to your **HTML** form:
172-
```html
173-
<html>
174-
<body>
175-
<form method="POST">
64+
<form method='POST' action='/my-form-post-endpoint'>
17665
...
177-
<div class="g-Turnstile" data-sitekey="your_site_key"></div>
66+
<div class="cf-turnstile" data-sitekey="your_site_key"></div>
17867
<input type="submit" value="Submit">
17968
</form>
180-
181-
<script src="https://www.google.com/Turnstile/api.js" async defer></script>
18269
</body>
18370
</html>
18471
```
185-
72+
When the user visits the **HTML** form, a hidden form field `cf-turnstile-response` will be added to the **HTML** form above. The `cf-turnstile-response` represents a token of the captcha challenge result and will need to be verified server-side when the **HTML** form is posted to your server.
18673

18774
### Verifying the POST Server-side
188-
When the `POST` is received on the server:
189-
1. Get the client's IP address. If you're using **CloudFlare**, be sure to use the [`CF-Connecting-IP` header value][0].
190-
2. Extract the `g-Turnstile-response` (Client Response) **HTML** form field.
191-
3. Use the `TurnstileService` to verify the client's **Turnstile** is valid.
75+
When the **HTML** form `POST` is received on the server:
76+
1. Get the client's IP address. If you're using **Cloudflare**, be sure to use the [`CF-Connecting-IP` header value][0].
77+
2. Extract the hidden form field `cf-turnstile-response` from the browser's form POST submission.
78+
3. Use the `TurnstileService` to verify that the client's **Turnstile** `cf-turnstile-response` challenge is valid.
19279

193-
The following example shows how to verify the captcha during an **HTTP** `POST` back in **ASP.NET Core: Razor Pages**.
80+
The following example shows how to verify the captcha during an **HTTP** form `POST` back in **ASP.NET Core: Razor Pages**.
19481

19582
```csharp
19683
//1. Get the client IP address in your chosen web framework
197-
string clientIp = GetClientIpAddress();
198-
string captchaResponse = null;
19984
string secret = "your_secret_key";
85+
string clientIp = GetClientIpAddress();
86+
string browserChallengeToken = null;
20087

201-
//2. Extract the `g-Turnstile-response` field from the HTML form in your chosen web framework
202-
if( this.Request.Form.TryGetValue(Constants.ClientResponseKey, out var formField) )
88+
//2. Extract the `cf-turnstile-response` hidden field from the HTML form in your chosen web framework
89+
//Tip: You can also use Constants.ClientResponseFormKey instead of the magic string below
90+
if( this.Request.Form.TryGetValue("cf-turnstile-response", out var hiddenFormField) )
20391
{
204-
capthcaResponse = formField;
92+
browserChallengeToken = hiddenFormField;
20593
}
20694

207-
//3. Validate the Turnstile with Google
208-
var captchaApi = new TurnstileService();
209-
var isValid = await captchaApi.Verify2Async(capthcaResponse, clientIp, secret);
210-
if( !isValid )
95+
//3. Validate the Turnstile challenge with Cloudflare
96+
var turnstileApi = new TurnstileService();
97+
var verifyResult = await turnstileApi.VerifyAsync(browserChallengeToken, secret, clientIp);
98+
if( verifyResult.IsSuccess is false )
21199
{
212-
this.ModelState.AddModelError("captcha", "The Turnstile is not valid.");
100+
this.ModelState.AddModelError("captcha", "The Cloudflare challenge is not valid.");
213101
return new BadRequestResult();
214102
}
215103
else{
216104
//continue processing, everything is okay!
217105
}
218106
```
219107

220-
<details><summary>GetClientIpAddress() in ASP.NET Core</summary>
108+
**Notes:**
109+
* The `TurnstileService.VerifyAsync` supports an optional idempotency parameter; you can read more about that [here](https://developers.cloudflare.com/turnstile/get-started/server-side-validation/#accepted-parameters).
110+
* The `clientIp` is technically optional but providing the client's IP address prevents abuses by ensuring that the current HTTP request is the one that received the token.
111+
112+
<details><summary><b>GetClientIpAddress() in ASP.NET Core</b></summary>
221113
<p>
222114

223-
**Note:** If your site is behind CloudFlare, be sure you're suing the [`CF-Connecting-IP` header value][0] instead.
115+
**Note:** If your site is behind Cloudflare, be sure you're using the [`CF-Connecting-IP` header value][0] instead.
224116

225117
```csharp
226118
public string GetClientIpAddress(){
227119
return this.HttpContext.Connection.RemoteIpAddress.ToString();
228120
}
229121
```
230-
231122
</p>
232123
</details>
233124

234-
<details><summary>GetClientIpAddress() in ASP.NET WebForms</summary>
125+
<details><summary><b>GetClientIpAddress() in ASP.NET WebForms</b></summary>
235126
<p>
236127

237-
**Note:** If your site is behind CloudFlare, be sure you're suing the [`CF-Connecting-IP` header value][0] instead.
128+
**Note:** If your site is behind Cloudflare, be sure you're using the [`CF-Connecting-IP` header value][0] instead.
238129

239130
```csharp
240131
public string GetClientIpAddress(){
241132
return this.Request.UserHostAddress;
242133
}
243134
```
244-
245135
</p>
246-
</details>
136+
</details>
247137

248138
That's it! **Happy verifying!** :tada:
249139

250-
251140
Building
252141
--------
253142
* Download the source code.

0 commit comments

Comments
 (0)