{"id":15036238,"url":"https://github.com/bitarmory/recaptcha","last_synced_at":"2025-04-13T06:42:00.760Z","repository":{"id":45649991,"uuid":"133585877","full_name":"BitArmory/ReCaptcha","owner":"BitArmory","description":":recycle: A friction-less C# HTTP verification client for Google's reCAPTCHA API.","archived":false,"fork":false,"pushed_at":"2024-09-02T22:09:01.000Z","size":817,"stargazers_count":45,"open_issues_count":3,"forks_count":11,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-26T23:09:05.826Z","etag":null,"topics":["antispam","csharp","csharp-library","google","recaptcha"],"latest_commit_sha":null,"homepage":"https://www.google.com/recaptcha","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/BitArmory.png","metadata":{"files":{"readme":"README.md","changelog":"HISTORY.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"bchavez"}},"created_at":"2018-05-15T23:56:30.000Z","updated_at":"2024-09-02T22:09:04.000Z","dependencies_parsed_at":"2024-06-18T16:42:58.703Z","dependency_job_id":"5346a0af-eeea-4d81-9dbc-0d2c4c9d1cc8","html_url":"https://github.com/BitArmory/ReCaptcha","commit_stats":{"total_commits":40,"total_committers":1,"mean_commits":40.0,"dds":0.0,"last_synced_commit":"6802518f77a17404953e4d87852d52e42977ef94"},"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BitArmory%2FReCaptcha","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BitArmory%2FReCaptcha/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BitArmory%2FReCaptcha/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BitArmory%2FReCaptcha/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/BitArmory","download_url":"https://codeload.github.com/BitArmory/ReCaptcha/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248675440,"owners_count":21143763,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["antispam","csharp","csharp-library","google","recaptcha"],"created_at":"2024-09-24T20:30:36.050Z","updated_at":"2025-04-13T06:42:00.722Z","avatar_url":"https://github.com/BitArmory.png","language":"C#","funding_links":["https://github.com/sponsors/bchavez"],"categories":[],"sub_categories":[],"readme":"[![Build status](https://ci.appveyor.com/api/projects/status/pw7x5xdqcvqrsrml/branch/master?svg=true)](https://ci.appveyor.com/project/BitArmory/recaptcha/branch/master) [![Nuget](https://img.shields.io/nuget/v/BitArmory.ReCaptcha.svg)](https://www.nuget.org/packages/BitArmory.ReCaptcha/) [![Users](https://img.shields.io/nuget/dt/BitArmory.ReCaptcha.svg)](https://www.nuget.org/packages/BitArmory.ReCaptcha/) \u003cimg src=\"https://raw.githubusercontent.com/BitArmory/ReCaptcha/master/docs/recaptcha.png\" align='right' /\u003e\r\n\r\nBitArmory.ReCaptcha for .NET and C#\r\n===================================\r\n\r\nProject Description\r\n-------------------\r\n:recycle: A minimal, no-drama, friction-less **C#** **HTTP** verification client for **Google**'s [**reCAPTCHA** API](https://www.google.com/recaptcha).\r\n\r\n:loudspeaker: ***HEY!*** Be sure to checkout these other captcha integrations:\r\n\r\n* [**BitArmory.Turnstile**](https://github.com/BitArmory/Turnstile) - Cloudflare's captcha verification service; a good alternative to Google's reCAPTCHA!\r\n\r\nThe 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**. \r\n\r\nFurthermore, 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](https://support.cloudflare.com/hc/en-us/articles/200170986-How-does-Cloudflare-handle-HTTP-Request-headers) is the ***real*** IP address of the visitor on your site.\r\n\r\n**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.\r\n\r\n#### Supported Platforms\r\n* **.NET Standard 1.3** or later\r\n* **.NET Framework 4.5** or later\r\n\r\n#### Supported reCAPTCHA Versions\r\n* [**reCAPTCHA v2 (I'm not a robot)**][2] \r\n* [**reCAPTCHA v3 (Invisible)**][3]\r\n\r\n#### Crypto Tip Jar\r\n\u003ca href=\"https://commerce.coinbase.com/checkout/f78fc08f-f34f-40c5-8262-8595c3492f3a\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/BitArmory/ReCaptcha/master/docs/tipjar.png\" /\u003e\u003c/a\u003e\r\n* :dog2: **Dogecoin**: `DGVC2drEMt41sEzEHSsiE3VTrgsQxGn5qe`\r\n\r\n\r\n### Download \u0026 Install\r\n**NuGet Package [BitArmory.ReCaptcha](https://www.nuget.org/packages/BitArmory.ReCaptcha/)**\r\n\r\n```powershell\r\nInstall-Package BitArmory.ReCaptcha\r\n```\r\n\r\nFull Examples\r\n--------\r\nVarious full and complete examples can be found here:\r\n\r\n#### reCAPTCHA v3 (Invisible)\r\n* [ASP.NET WebForms for Full Framework](https://github.com/BitArmory/ReCaptcha/tree/master/Examples/ReCaptchaV3.WebForms)\r\n\r\n\r\nGeneral Usage\r\n-------------\r\n### Getting Started\r\nYou'll need to create **reCAPTCHA** account. You can sign up [here](https://www.google.com/recaptcha)! After you sign up and setup your domain, you'll have two important pieces of information:\r\n1. Your `site` key\r\n2. Your `secret` key\r\n\r\nThis library supports both: \r\n* [**reCAPTCHA v2 (I'm not a robot)**][2] \r\n* [**reCAPTCHA v3 (Invisible)**][3].\r\n\r\n\r\n## reCAPTCHA v3 (Invisible)\r\n### Client-side Setup\r\n\r\nBe sure to checkout [this video that describes how reCAPTCHA v3 works](https://www.youtube.com/watch?v=tbvxFW4UJdU) before implementing.\r\n\r\nThen, on every page of your website, add the following JavaScript:\r\n```html\r\n\u003chtml\u003e\r\n  \u003chead\u003e\r\n    \u003cscript src='https://www.google.com/recaptcha/api.js?render=GOOGLE_SITE_KEY'\u003e\u003c/script\u003e\r\n  \u003c/head\u003e\r\n  \u003cbody\u003e\r\n    ...\r\n    \u003cscript\u003e\r\n        grecaptcha.ready(function() {\r\n          grecaptcha.execute('GOOGLE_SITE_KEY', {action: 'TAG'});\r\n        });\r\n    \u003c/script\u003e\r\n  \u003c/body\u003e\r\n\u003c/html\u003e\r\n```\r\nEvery page should call `grecaptcha.execute` with some unique **action** `TAG`. [Read more about actions in the official docs here](https://developers.google.com/recaptcha/docs/v3#actions).\r\n\r\nWhen 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:\r\n\r\n```html\r\n\u003chtml\u003e\r\n  \u003cbody\u003e\r\n    \u003cform action='/do-post' method='POST'\u003e\r\n      \u003cinput id=\"captcha\" type=\"hidden\" name=\"captcha\" value=\"\" /\u003e\r\n    \u003c/form\u003e\r\n    \u003cscript\u003e\r\n      function ExecuteReCaptcha_OnSome_ButtonAction(){\r\n        grecaptcha.ready(function() {\r\n          grecaptcha.execute('GOOGLE_SITE_KEY', {action: 'SomeAction'})\r\n            .then(function(token) {\r\n               // Set `token` in a hidden form input.\r\n               $(\"#captcha\").val(token);\r\n               \r\n               //And finally submit the form by firing\r\n               //off the HTTP POST over the wire at this\r\n               //exact moment in time here.\r\n            });\r\n        });\r\n      }\r\n    \u003c/script\u003e\r\n  \u003c/body\u003e\r\n\u003c/html\u003e\r\n```\r\nYou'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.\r\n\r\nTherefore, you should execute the `ExecuteReCaptcha_OnSome_ButtonAction()` function on some `onclick=` event to get a fresh token before the form is submitted.\r\n\r\nAlso, keep in mind, `grecaptcha.execute()` returns a **JavaScript Promise**. You won't have a valid token in your `\u003cform\u003e` 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. \r\n\r\n### Verifying the POST Server-side\r\nWhen the `POST` is received on the server:\r\n1. Get the client's IP address. If you're using **CloudFlare**, be sure to use the [`CF-Connecting-IP` header value][0].\r\n2. Extract the `#captcha` value (client token) in the hidden **HTML** form field.\r\n3. Use the `ReCaptchaService` to verify the client's **reCAPTCHA** is valid.\r\n\r\n```csharp\r\n//1. Get the client IP address in your chosen web framework\r\nstring clientIp = GetClientIpAddress();\r\nstring token = null;\r\nstring secret = \"your_secret_key\";\r\n\r\n//2. Extract the `#captcha` field from the hidden HTML form in your chosen web framework\r\nif( this.Request.Form.TryGetValue(\"captcha\", out var formField) )\r\n{\r\n   token = formField;\r\n}\r\n\r\n//3. Validate the reCAPTCHA with Google\r\nvar captchaApi = new ReCaptchaService();\r\nvar result = await captchaApi.Verify3Async(token, clientIp, secret);\r\n\r\nif( !result.IsSuccess || result.Action != \"SOME_ACTION\" || result.Score \u003c 0.5 )\r\n{\r\n   // The POST is not valid\r\n   return new BadRequestResult();\r\n}\r\nelse{\r\n   //continue processing, everything is okay!\r\n}\r\n```\r\n\r\n\u003cdetails\u003e\u003csummary\u003eGetClientIpAddress() in ASP.NET Core\u003c/summary\u003e\r\n\u003cp\u003e\r\n\r\n**Note:** If your site is behind CloudFlare, be sure you're suing the [`CF-Connecting-IP` header value][0] instead.\r\n\r\n```csharp\r\npublic string GetClientIpAddress(){\r\n   return this.HttpContext.Connection.RemoteIpAddress.ToString();\r\n}\r\n```\r\n\r\n\u003c/p\u003e\r\n\u003c/details\u003e\r\n\r\n\u003cdetails\u003e\u003csummary\u003eGetClientIpAddress() in ASP.NET WebForms\u003c/summary\u003e\r\n\u003cp\u003e\r\n\r\n**Note:** If your site is behind CloudFlare, be sure you're suing the [`CF-Connecting-IP` header value][0] instead.\r\n\r\n```csharp\r\npublic string GetClientIpAddress(){\r\n   return this.Request.UserHostAddress;\r\n}\r\n```\r\n\r\n\u003c/p\u003e\r\n\u003c/details\u003e         \r\n\r\nYou'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 \u003e 0.5`; anything less is probably a bot.\r\n\r\n## reCAPTCHA v2 (I'm not a robot)\r\n### Client-side Setup\r\nAdd the following `\u003cdiv class=\"g-recaptcha\"\u003e` and `\u003cscript\u003e` tags to your **HTML** form:\r\n```html\r\n\u003chtml\u003e\r\n  \u003cbody\u003e\r\n    \u003cform method=\"POST\"\u003e\r\n        ...\r\n        \u003cdiv class=\"g-recaptcha\" data-sitekey=\"your_site_key\"\u003e\u003c/div\u003e\r\n        \u003cinput type=\"submit\" value=\"Submit\"\u003e\r\n    \u003c/form\u003e\r\n\r\n    \u003cscript src=\"https://www.google.com/recaptcha/api.js\" async defer\u003e\u003c/script\u003e\r\n  \u003c/body\u003e\r\n\u003c/html\u003e\r\n```\r\n\r\n\r\n### Verifying the POST Server-side\r\nWhen the `POST` is received on the server:\r\n1. Get the client's IP address. If you're using **CloudFlare**, be sure to use the [`CF-Connecting-IP` header value][0].\r\n2. Extract the `g-recaptcha-response` (Client Response) **HTML** form field.\r\n3. Use the `ReCaptchaService` to verify the client's **reCAPTCHA** is valid.\r\n\r\nThe following example shows how to verify the captcha during an **HTTP** `POST` back in **ASP.NET Core: Razor Pages**.\r\n\r\n```csharp\r\n//1. Get the client IP address in your chosen web framework\r\nstring clientIp = GetClientIpAddress();\r\nstring captchaResponse = null;\r\nstring secret = \"your_secret_key\";\r\n\r\n//2. Extract the `g-recaptcha-response` field from the HTML form in your chosen web framework\r\nif( this.Request.Form.TryGetValue(Constants.ClientResponseKey, out var formField) )\r\n{\r\n   capthcaResponse = formField;\r\n}\r\n\r\n//3. Validate the reCAPTCHA with Google\r\nvar captchaApi = new ReCaptchaService();\r\nvar isValid = await captchaApi.Verify2Async(capthcaResponse, clientIp, secret);\r\nif( !isValid )\r\n{\r\n   this.ModelState.AddModelError(\"captcha\", \"The reCAPTCHA is not valid.\");\r\n   return new BadRequestResult();\r\n}\r\nelse{\r\n   //continue processing, everything is okay!\r\n}\r\n```\r\n\r\n\u003cdetails\u003e\u003csummary\u003eGetClientIpAddress() in ASP.NET Core\u003c/summary\u003e\r\n\u003cp\u003e\r\n\r\n**Note:** If your site is behind CloudFlare, be sure you're suing the [`CF-Connecting-IP` header value][0] instead.\r\n\r\n```csharp\r\npublic string GetClientIpAddress(){\r\n   return this.HttpContext.Connection.RemoteIpAddress.ToString();\r\n}\r\n```\r\n\r\n\u003c/p\u003e\r\n\u003c/details\u003e\r\n\r\n\u003cdetails\u003e\u003csummary\u003eGetClientIpAddress() in ASP.NET WebForms\u003c/summary\u003e\r\n\u003cp\u003e\r\n\r\n**Note:** If your site is behind CloudFlare, be sure you're suing the [`CF-Connecting-IP` header value][0] instead.\r\n\r\n```csharp\r\npublic string GetClientIpAddress(){\r\n   return this.Request.UserHostAddress;\r\n}\r\n```\r\n\r\n\u003c/p\u003e\r\n\u003c/details\u003e    \r\n\r\nThat's it! **Happy verifying!** :tada:\r\n\r\n\r\nBuilding\r\n--------\r\n* Download the source code.\r\n* Run `build.cmd`.\r\n\r\nUpon 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`.\r\n\r\n\r\n\r\n[0]:https://support.cloudflare.com/hc/en-us/articles/200170986-How-does-Cloudflare-handle-HTTP-Request-headers\r\n[2]:#recaptcha-v2-im-not-a-robot\r\n[3]:#recaptcha-v3-invisible-1","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbitarmory%2Frecaptcha","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbitarmory%2Frecaptcha","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbitarmory%2Frecaptcha/lists"}