A lightweight library for server-side verification of Google reCAPTCHA v2 and v3 response tokens in .NET applications.
Starting with version 3.0.0, Recaptcha.Verify.Net supports the following platforms:
- .NET 8
- .NET 9
- .NET 10
For .NET Framework or for other older .NET versions, please use a version below 3.0.0.
- Installation
- Basic Usage
- Verification Process
- Configuring Actions and Score Thresholds
- Response Customization
- Handling Exceptions
- Examples
The package is available on NuGet and can be installed through your preferred method.
- Via Package Manager Console
PM> Install-Package Recaptcha.Verify.Net -Version 3.0.0
- Via .NET CLI
dotnet add package Recaptcha.Verify.Net --version 3.0.0
- Adding reference to project file
<PackageReference Include="Recaptcha.Verify.Net" Version="3.0.0" />
- Add settings to appsettings.json. Specify reCAPTCHA secret token and a way to receive reCAPTCHA response token from request.
{ "Recaptcha": { "SecretKey": "<recaptcha secret key>", "AttributeOptions": { "ResponseTokenNameInHeader": "X-Recaptcha-Token", } } } - Register library services for DI.
services.AddRecaptcha(configuration.GetSection("Recaptcha"));
- Apply the
RecaptchaAttributeto protected endpoints. Specify action and score threshold for validating v3 verification result.[Recaptcha("login", 0.5f)] [HttpPost("login")] public async Task<IActionResult> Login([FromBody] Credentials credentials, CancellationToken cancellationToken) { // Process login return Ok(); }
The verification of a reCAPTCHA response token consists of three sequential steps:
- Extracting the token from the request
- Verifying the token with Google's API
- Validating the verification result
Token extraction is performed by services implementing the IRecaptchaTokenExtractor interface.
The RecaptchaAttribute uses registered extractors sequentially until the first one successfully retrieves a token.
The library provides these extractors out of the box:
| Extractor | Description | Registration Method |
|---|---|---|
FormTokenExtractor |
Extracts token from request form data | services.AddRecaptchaFormTokenExtractor(name) |
HeaderTokenExtractor |
Extracts token from request headers | services.AddRecaptchaHeaderTokenExtractor(name) |
QueryTokenExtractor |
Extracts token from query parameters | services.AddRecaptchaQueryTokenExtractor(name) |
ActionArgumentsTokenExtractor |
Extracts token from parsed action arguments | services.AddRecaptchaActionArgumentsTokenExtractor(name) or services.AddRecaptchaActionArgumentsTokenExtractor(delegate) |
ExecutingContextTokenExtractor |
Extracts token from the executing context | services.AddRecaptchaExecutingContextTokenExtractor(delegate) |
When using services.AddRecaptcha(), extractors are automatically registered based on provided configuration RecaptchaOptions.AttributeOptions:
- if
ResponseTokenNameInFormis not empty then registersFormTokenExtractor; - if
ResponseTokenNameInHeaderis not empty then registersHeaderTokenExtractor; - if
ResponseTokenNameInQueryis not empty then registersQueryTokenExtractor; - if
GetResponseTokenFromActionArgumentsdelegate is not empty then registersActionArgumentsTokenExtractor; - if
GetResponseTokenFromExecutingContextdelegate is not empty then registersExecutingContextTokenExtractor.
- Query Parameters: Avoid using
QueryTokenExtractoras tokens in URLs may be logged or cached. Prefer headers or body. - Request Body: Use
ActionArgumentsTokenExtractorfor body tokens, since the request stream is already read and models are populated by this stage. - Custom Logic: For complex extraction scenarios, either:
- Use
ExecutingContextTokenExtractorwith delegate; - Implement your own
IRecaptchaTokenExtractorand register it in the DI container.
- Use
services.AddRecaptchaHeaderTokenExtractor("X-Recaptcha-Token");
services.AddRecaptchaActionArgumentsTokenExtractor(args =>
args.TryGetValue("recaptchaToken", out var token) ? token?.ToString() : null);Token verification is performed by the RecaptchaVerificationService, which communicates with Google's reCAPTCHA API through the RecaptchaClient.
The service returns a VerifyResponse object containing verification details.
| Property | Type | Description |
|---|---|---|
Success |
bool |
Indicates whether this request was a valid reCAPTCHA token for your site |
Score |
float? |
reCAPTCHA v3 only: Confidence score from 0.0 (likely bot) to 1.0 (likely human) |
Action |
string? |
reCAPTCHA v3 only: The action name specified during client-side execution |
ChallengeTs |
DateTime |
Timestamp when the challenge was loaded |
Hostname |
string? |
The hostname of the site where the reCAPTCHA was solved |
ApkPackageName |
string? |
For Android applications: the package name of the APK |
ErrorCodes |
IReadOnlyCollection<string>? |
Raw error codes returned by Google's API |
Errors |
IReadOnlyCollection<VerifyError>? |
Parsed error codes as VerifyError enum values |
By default, Google validates that the Hostname or ApkPackageName matches your registered domains/packages. This security feature can be disabled in the reCAPTCHA admin console if needed.
Verifying the user's response
reCAPTCHA v3 Actions
reCAPTCHA v3 Site Verify Response
Domain/Package Name Validation
After token verification, the result must be validated.
This is performed by the RecaptchaVerificationResultValidationService.
For reCAPTCHA v3 (detected by the presence of a Score value), the service checks:
- Action Matching: Ensures the returned action matches the expected value
- Provided in the
RecaptchaAttributeconstructor - Global action
RecaptchaOptions.Action
- Provided in the
- Score Threshold: Ensures the confidence score meets the required threshold
- Provided in the
RecaptchaAttributeconstructor - Global threshold
RecaptchaOptions.ScoreThreshold - Action-specific threshold specified in
RecaptchaOptions.ActionsScoreThresholdsdictionary
- Provided in the
The service returns a ValidationResult object with detailed validation status.
| Property | Type | Description |
|---|---|---|
ResponseSuccessful |
bool |
Indicates whether verify request was successful |
IsV3 |
bool |
Indicates whether this is a reCAPTCHA v3 verification (based on score presence) |
ActionMatches |
bool |
Indicates whether the action matches the expected value |
ScoreSatisfies |
bool |
Indicates whether the score meets the required threshold |
Success |
bool |
Overall validation result — true only when all applicable checks pass |
For reCAPTCHA v3 validation, both an action and a score threshold must be specified. These can be configured either:
- Per-endpoint via the
RecaptchaAttributeconstructor - Globally via RecaptchaOptions
The system follows this priority order (highest to lowest):
For Actions:
RecaptchaAttributeconstructor valueRecaptchaOptions.Action- global default action for all v3 validations
For Score Thresholds:
RecaptchaAttributeconstructor valueRecaptchaOptions.ActionsScoreThresholds[action]- action-specific thresholds mapRecaptchaOptions.ScoreThreshold- global default score threshold (By default, you can use a threshold of 0.5)
{
"Recaptcha": {
"Action": "default_action",
"ScoreThreshold": 0.5,
"ActionsScoreThresholds": {
"login": 0.8,
"comment": 0.3
}
}
}// Uses global action "default_action" and threshold 0.5
[Recaptcha]
public IActionResult DefaultEndpoint() { ... }
// Uses action "register" with global threshold 0.5
[Recaptcha("register")]
public IActionResult Register() { ... }
// Uses action "login" with threshold from ActionsScoreThresholds (0.8)
[Recaptcha("login")]
public IActionResult Login() { ... }
// Uses action "comment" with explicit threshold 0.4 (overrides the map)
[Recaptcha("comment", 0.4)]
public IActionResult AddComment() { ... }When reCAPTCHA verification fails or an exception occurs (will not catch exceptions in future versions), the library returns a 400 Bad Request response by default.
Message specified in RecaptchaOptions.VerificationFailedMessage which default value is "Recaptcha verification failed".
| Delegate | Trigger | Status |
|---|---|---|
OnVerificationFailed |
When token verification or validation fails | Active |
OnRecaptchaServiceException |
When RecaptchaServiceException is thrown |
Deprecated |
OnException |
When any unhandled exception occurs | Deprecated |
OnReturnBadRequest |
When any failure or exception occurs | Deprecated |
The IActionResult returned by these delegates is used as the HTTP response.
Delegates may also throw exceptions, which will not be caught by RecaptchaAttribute.
Deprecated delegates will be removed in future versions in favor of exception handling middleware.
Library can produce following exceptions
| Exception | Description |
|---|---|
RecaptchaServiceException |
Base recaptcha exception. Other exceptions are inherited from this. |
RecaptchaServiceConfigurationException |
Base recaptcha exception for invalid configuration. |
EmptyActionException |
This exception is thrown when the action passed in function is empty. |
MinScoreNotSpecifiedException |
This exception is thrown when minimal score was not specified and request had score value (used V3 reCAPTCHA). |
SecretKeyNotSpecifiedException |
This exception is thrown when secret key was not specified in options or request params. |
TokenExtractorNotFound |
This exception is thrown when no ITokenExtractor implementation is registered in DI. |
RecaptchaServiceProcessingException |
Base recaptcha exception for errors while processing token verification and result checking. |
EmptyCaptchaAnswerException |
This exception is thrown when captcha answer passed in function is empty. |
EmptyResponseException |
This exception is thrown when verification request response is empty. When thrown, it is wrapped in VerifyRequestException. |
VerifyRequestException |
This exception is thrown when verification request failed. Stores inner exception. |
UnknownErrorKeyException |
This exception is thrown when verification response error key is unknown. |
RecaptchaUnknownException |
This exception is thrown when an unexpected exception is catched during processing captcha. |
Examples could be found in library repository:
- Recaptcha.Verify.Net.ConsoleApp (.NET 10)
- Recaptcha.Verify.Net.AspNetCoreAngular (ASP.NET + Angular)