♻️ A minimal, no-drama, friction-less C# HTTP verification client for Google's reCAPTCHA API.
📢 HEY! Be sure to checkout these other captcha integrations:
- BitArmory.Turnstile - Cloudflare's captcha verification service; a good alternative to Google's reCAPTCHA!
The problem with current ReCaptcha 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.
Furthermore, current reCAPTCHA libraries for .NET are hard coded against the HttpContext.Request
to retrieve the remote IP address of the visitor. Unfortunately, this method doesn't work if your website is behind a service like CloudFlare where the CF-Connecting-IP
header value is the real IP address of the visitor on your site.
BitArmory.ReCaptcha is a minimal library that works across all .NET web frameworks without taking a hard dependency on any web framework. If you want to leverage platform specific features, like MVC Action Filters, you'll need to implement your own ActionFilter
that leverages the functionality in this library.
- .NET Standard 1.3 or later
- .NET Framework 4.5 or later
- 🐕 Dogecoin:
DGVC2drEMt41sEzEHSsiE3VTrgsQxGn5qe
NuGet Package BitArmory.ReCaptcha
Install-Package BitArmory.ReCaptcha
Various full and complete examples can be found here:
You'll need to create reCAPTCHA account. You can sign up here! After you sign up and setup your domain, you'll have two important pieces of information:
- Your
site
key - Your
secret
key
This library supports both:
Be sure to checkout this video that describes how reCAPTCHA v3 works before implementing.
Then, on every page of your website, add the following JavaScript:
<html>
<head>
<script src='https://www.google.com/recaptcha/api.js?render=GOOGLE_SITE_KEY'></script>
</head>
<body>
...
<script>
grecaptcha.ready(function() {
grecaptcha.execute('GOOGLE_SITE_KEY', {action: 'TAG'});
});
</script>
</body>
</html>
Every page should call grecaptcha.execute
with some unique action TAG
. Read more about actions in the official docs here.
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:
<html>
<body>
<form action='/do-post' method='POST'>
<input id="captcha" type="hidden" name="captcha" value="" />
</form>
<script>
function ExecuteReCaptcha_OnSome_ButtonAction(){
grecaptcha.ready(function() {
grecaptcha.execute('GOOGLE_SITE_KEY', {action: 'SomeAction'})
.then(function(token) {
// Set `token` in a hidden form input.
$("#captcha").val(token);
//And finally submit the form by firing
//off the HTTP POST over the wire at this
//exact moment in time here.
});
});
}
</script>
</body>
</html>
You'll need to execute ExecuteReCaptcha_OnSome_ButtonAction()
function the moment the user decides to submit your form. Otherwise, if you run grecaptcha.*
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.
Therefore, you should execute the ExecuteReCaptcha_OnSome_ButtonAction()
function on some onclick=
event to get a fresh token before the form is submitted.
Also, keep in mind, grecaptcha.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.
When the POST
is received on the server:
- Get the client's IP address. If you're using CloudFlare, be sure to use the
CF-Connecting-IP
header value. - Extract the
#captcha
value (client token) in the hidden HTML form field. - Use the
ReCaptchaService
to verify the client's reCAPTCHA is valid.
//1. Get the client IP address in your chosen web framework
string clientIp = GetClientIpAddress();
string token = null;
string secret = "your_secret_key";
//2. Extract the `#captcha` field from the hidden HTML form in your chosen web framework
if( this.Request.Form.TryGetValue("captcha", out var formField) )
{
token = formField;
}
//3. Validate the reCAPTCHA with Google
var captchaApi = new ReCaptchaService();
var result = await captchaApi.Verify3Async(token, clientIp, secret);
if( !result.IsSuccess || result.Action != "SOME_ACTION" || result.Score < 0.5 )
{
// The POST is not valid
return new BadRequestResult();
}
else{
//continue processing, everything is okay!
}
GetClientIpAddress() in ASP.NET Core
Note: If your site is behind CloudFlare, be sure you're suing the CF-Connecting-IP
header value instead.
public string GetClientIpAddress(){
return this.HttpContext.Connection.RemoteIpAddress.ToString();
}
GetClientIpAddress() in ASP.NET WebForms
Note: If your site is behind CloudFlare, be sure you're suing the CF-Connecting-IP
header value instead.
public string GetClientIpAddress(){
return this.Request.UserHostAddress;
}
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.
Add the following <div class="g-recaptcha">
and <script>
tags to your HTML form:
<html>
<body>
<form method="POST">
...
<div class="g-recaptcha" data-sitekey="your_site_key"></div>
<input type="submit" value="Submit">
</form>
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
</body>
</html>
When the POST
is received on the server:
- Get the client's IP address. If you're using CloudFlare, be sure to use the
CF-Connecting-IP
header value. - Extract the
g-recaptcha-response
(Client Response) HTML form field. - Use the
ReCaptchaService
to verify the client's reCAPTCHA is valid.
The following example shows how to verify the captcha during an HTTP POST
back in ASP.NET Core: Razor Pages.
//1. Get the client IP address in your chosen web framework
string clientIp = GetClientIpAddress();
string captchaResponse = null;
string secret = "your_secret_key";
//2. Extract the `g-recaptcha-response` field from the HTML form in your chosen web framework
if( this.Request.Form.TryGetValue(Constants.ClientResponseKey, out var formField) )
{
capthcaResponse = formField;
}
//3. Validate the reCAPTCHA with Google
var captchaApi = new ReCaptchaService();
var isValid = await captchaApi.Verify2Async(capthcaResponse, clientIp, secret);
if( !isValid )
{
this.ModelState.AddModelError("captcha", "The reCAPTCHA is not valid.");
return new BadRequestResult();
}
else{
//continue processing, everything is okay!
}
GetClientIpAddress() in ASP.NET Core
Note: If your site is behind CloudFlare, be sure you're suing the CF-Connecting-IP
header value instead.
public string GetClientIpAddress(){
return this.HttpContext.Connection.RemoteIpAddress.ToString();
}
GetClientIpAddress() in ASP.NET WebForms
Note: If your site is behind CloudFlare, be sure you're suing the CF-Connecting-IP
header value instead.
public string GetClientIpAddress(){
return this.Request.UserHostAddress;
}
That's it! Happy verifying! 🎉
- Download the source code.
- Run
build.cmd
.
Upon successful build, the results will be in the \__compile
directory. If you want to build NuGet packages, run build.cmd pack
and the NuGet packages will be in __package
.