Edi.Captcha 5.0.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package Edi.Captcha --version 5.0.0
                    
NuGet\Install-Package Edi.Captcha -Version 5.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Edi.Captcha" Version="5.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Edi.Captcha" Version="5.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Edi.Captcha" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Edi.Captcha --version 5.0.0
                    
#r "nuget: Edi.Captcha, 5.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Edi.Captcha@5.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Edi.Captcha&version=5.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Edi.Captcha&version=5.0.0
                    
Install as a Cake Tool

Edi.Captcha.AspNetCore

The Captcha module used in my blog

.NET

NuGet

Install

NuGet Package Manager

Install-Package Edi.Captcha

or .NET CLI

dotnet add package Edi.Captcha

Session-Based Captcha (Traditional Approach)

1. Register in DI

services.AddSession(options =>
{
    options.IdleTimeout = TimeSpan.FromMinutes(20);
    options.Cookie.HttpOnly = true;
});

services.AddSessionBasedCaptcha();
// Don't forget to add this line in your `Configure` method.
 app.UseSession();

or you can customize the options

services.AddSessionBasedCaptcha(option =>
{
    option.Letters = "2346789ABCDEFGHJKLMNPRTUVWXYZ";
    option.SessionName = "CaptchaCode";
    option.CodeLength = 4;
});

2. Generate Image

Using MVC Controller
private readonly ISessionBasedCaptcha _captcha;

public SomeController(ISessionBasedCaptcha captcha)
{
    _captcha = captcha;
}

[Route("get-captcha-image")]
public IActionResult GetCaptchaImage()
{
    var s = _captcha.GenerateCaptchaImageFileStream(
        HttpContext.Session,
        100,
        36
    );
    return s;
}
Using Middleware
app.UseSession().UseCaptchaImage(options =>
{
    options.RequestPath = "/captcha-image";
    options.ImageHeight = 36;
    options.ImageWidth = 100;
});

3. Add CaptchaCode Property to Model

[Required]
[StringLength(4)]
public string CaptchaCode { get; set; }

5. View

<div class="col">
    <div class="input-group">
        <div class="input-group-prepend">
            <img id="img-captcha" src="~/captcha-image" />
        </div>
        <input type="text" 
               asp-for="CommentPostModel.CaptchaCode" 
               class="form-control" 
               placeholder="Captcha Code" 
               autocomplete="off" 
               minlength="4"
               maxlength="4" />
    </div>
    <span asp-validation-for="CommentPostModel.CaptchaCode" class="text-danger"></span>
</div>

6. Validate Input

_captcha.ValidateCaptchaCode(model.CommentPostModel.CaptchaCode, HttpContext.Session)

To make your code look more cool, you can also write an Action Filter like this:

public class ValidateCaptcha : ActionFilterAttribute
{
    private readonly ISessionBasedCaptcha _captcha;

    public ValidateCaptcha(ISessionBasedCaptcha captcha)
    {
        _captcha = captcha;
    }

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        var captchaedModel =
            context.ActionArguments.Where(p => p.Value is ICaptchable)
                                   .Select(x => x.Value as ICaptchable)
                                   .FirstOrDefault();

        if (null == captchaedModel)
        {
            context.ModelState.AddModelError(nameof(captchaedModel.CaptchaCode), "Captcha Code is required");
            context.Result = new BadRequestObjectResult(context.ModelState);
        }
        else
        {
            if (!_captcha.Validate(captchaedModel.CaptchaCode, context.HttpContext.Session))
            {
                context.ModelState.AddModelError(nameof(captchaedModel.CaptchaCode), "Wrong Captcha Code");
                context.Result = new ConflictObjectResult(context.ModelState);
            }
            else
            {
                base.OnActionExecuting(context);
            }
        }
    }
}

and then

services.AddScoped<ValidateCaptcha>();

and then


public class YourModelWithCaptchaCode : ICaptchable
{
    public string YourProperty { get; set; }

    [Required]
    [StringLength(4)]
    public string CaptchaCode { get; set; }
}

[ServiceFilter(typeof(ValidateCaptcha))]
public async Task<IActionResult> SomeAction(YourModelWithCaptchaCode model)
{
    // ....
}

Advantages of Stateless Captcha:

  • ✅ Works in clustered/load-balanced environments
  • ✅ No server-side session storage required
  • ✅ Built-in expiration through encryption
  • ✅ Secure token-based validation
  • ✅ Better scalability
  • ✅ Single API call for both token and image

1. Register in DI

services.AddStatelessCaptcha();

or with custom options:

services.AddStatelessCaptcha(options =>
{
    options.Letters = "2346789ABCDGHKMNPRUVWXYZ";
    options.CodeLength = 4;
    options.TokenExpiration = TimeSpan.FromMinutes(5);
});

2. Create Model with Token Support

public class StatelessHomeModel
{
    [Required]
    [StringLength(4)]
    public string CaptchaCode { get; set; }
    
    public string CaptchaToken { get; set; }
}

3. Example Controller

using Edi.Captcha.SampleApp.Models;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Diagnostics;

namespace Edi.Captcha.SampleApp.Controllers;

public class StatelessController(IStatelessCaptcha captcha) : Controller
{
    public IActionResult Index()
    {
        return View(new StatelessHomeModel());
    }

    [HttpPost]
    public IActionResult Index(StatelessHomeModel model)
    {
        if (ModelState.IsValid)
        {
            bool isValidCaptcha = captcha.Validate(model.CaptchaCode, model.CaptchaToken);
            return Content(isValidCaptcha ? "Success - Stateless captcha validated!" : "Invalid captcha code");
        }

        return BadRequest();
    }

    [Route("get-stateless-captcha")]
    public IActionResult GetStatelessCaptcha()
    {
        var result = captcha.GenerateCaptcha(100, 36);
        
        return Json(new { 
            token = result.Token, 
            imageBase64 = Convert.ToBase64String(result.ImageBytes)
        });
    }

    [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
    public IActionResult Error()
    {
        return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
    }
}

4. Example View

@model StatelessHomeModel
@{
    ViewData["Title"] = "Stateless Captcha Example";
}

<div class="text-center">
    <h1 class="display-4">Stateless Captcha Example</h1>
    <p>This example shows how to use stateless captcha that works in clustered environments.</p>
</div>

<div class="row">
    <div class="col-md-6 offset-md-3">
        <div class="card">
            <div class="card-header">
                <h5>Stateless Captcha Form</h5>
            </div>
            <div class="card-body">
                <form asp-action="Index" method="post" id="stateless-form">
                    <div class="form-group mb-3">
                        <label>Captcha Image:</label>
                        <div class="d-flex align-items-center">
                            <img id="captcha-image" src="" alt="Captcha" class="me-2" style="border: 1px solid #ccc;" />
                            <button type="button" class="btn btn-sm btn-outline-secondary" onclick="refreshCaptcha()">
                                🔄 Refresh
                            </button>
                        </div>
                        <small class="form-text text-muted">Click refresh to get a new captcha</small>
                    </div>

                    <div class="form-group mb-3">
                        <label asp-for="CaptchaCode">Enter Captcha Code:</label>
                        <input asp-for="CaptchaCode" class="form-control" placeholder="Enter the code from image" autocomplete="off" />
                        <span asp-validation-for="CaptchaCode" class="text-danger"></span>
                    </div>

                    <input type="hidden" asp-for="CaptchaToken" id="captcha-token" />

                    <div class="form-group">
                        <button type="submit" class="btn btn-primary">Submit</button>
                        <a asp-controller="Home" asp-action="Index" class="btn btn-secondary">Session-based Example</a>
                    </div>
                </form>

                <div class="mt-4">
                    <h6>Advantages of Stateless Captcha:</h6>
                    <ul class="small">
                        <li>✅ Works in clustered/load-balanced environments</li>
                        <li>✅ No server-side session storage required</li>
                        <li>✅ Built-in expiration through encryption</li>
                        <li>✅ Secure token-based validation</li>
                        <li>✅ Better scalability</li>
                        <li>✅ Single API call for both token and image</li>
                    </ul>
                </div>
            </div>
        </div>
    </div>
</div>

<script>
    async function refreshCaptcha() {
        try {
            const response = await fetch('/get-stateless-captcha');
            const data = await response.json();
            
            // Set the token for validation
            document.getElementById('captcha-token').value = data.token;
            
            // Set the image source using base64 data
            document.getElementById('captcha-image').src = `data:image/png;base64,${data.imageBase64}`;
            
            // Clear the input
            document.getElementById('CaptchaCode').value = '';
        } catch (error) {
            console.error('Error refreshing captcha:', error);
            alert('Failed to load captcha. Please try again.');
        }
    }

    // Initialize captcha on page load
    document.addEventListener('DOMContentLoaded', function() {
        refreshCaptcha();
    });
</script>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories (1)

Showing the top 1 popular GitHub repositories that depend on Edi.Captcha:

Repository Stars
EdiWang/Moonglade
Blog system of https://edi.wang, runs on Microsoft Azure
Version Downloads Last Updated
6.0.0 47 3/15/2026
5.3.0 1,659 11/13/2025
5.2.1 245 10/31/2025
5.2.0 268 10/14/2025
5.1.1 201 10/4/2025
5.1.0 153 10/3/2025
5.0.1 161 10/3/2025
5.0.0 324 10/3/2025
4.0.0 546 8/17/2025
3.26.4 354 7/31/2025
3.26.3 387 7/2/2025
3.26.2 445 6/6/2025
3.26.1 1,173 3/11/2025
3.26.0 499 2/8/2025
3.25.0 1,613 11/13/2024
3.24.0 1,550 8/11/2024
3.23.1 790 7/14/2024
3.23.0 313 7/8/2024
3.22.0 1,127 5/19/2024
3.21.2 1,534 3/11/2024
Loading failed