Skip to content

Commit

Permalink
Merge pull request #46 from argon-chat/feature/captcha
Browse files Browse the repository at this point in the history
Feature/captcha
  • Loading branch information
0xF6 authored Nov 15, 2024
2 parents b590dd6 + e8266ac commit 2f46943
Show file tree
Hide file tree
Showing 10 changed files with 115 additions and 470 deletions.
480 changes: 10 additions & 470 deletions .editorconfig

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions src/Argon.Api/Features/Captcha/CaptchaFeature.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace Argon.Api.Features.Captcha;

using Microsoft.Extensions.Configuration;

public static class CaptchaFeature
{
public static IServiceCollection AddCaptchaFeature(this WebApplicationBuilder builder)
{
var cfg = builder.Configuration.GetSection("Captcha");
builder.Services.Configure<CaptchaOptions>(cfg);
var kind = cfg.GetValue<CaptchaKind>("Kind");

return kind switch
{
CaptchaKind.NO_CAPTCHA => builder.Services.AddTransient<ICaptchaFeature, NullCaptcha>(),
CaptchaKind.CLOUDFLARE => builder.Services.AddTransient<ICaptchaFeature, CloudflareCaptcha>(),
CaptchaKind.YANDEX => builder.Services.AddTransient<ICaptchaFeature, YandexCaptcha>(),
_ => throw new ArgumentOutOfRangeException()
};
}
}
8 changes: 8 additions & 0 deletions src/Argon.Api/Features/Captcha/CaptchaKind.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Argon.Api.Features.Captcha;

public enum CaptchaKind
{
NO_CAPTCHA,
CLOUDFLARE,
YANDEX
}
9 changes: 9 additions & 0 deletions src/Argon.Api/Features/Captcha/CaptchaOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Argon.Api.Features.Captcha;

public class CaptchaOptions
{
public string SiteKey { get; set; }
public string SiteSecret { get; set; }
public string ChallengeEndpoint { get; set; }
public CaptchaKind Kind { get; set; }
}
41 changes: 41 additions & 0 deletions src/Argon.Api/Features/Captcha/CloudflareCaptcha.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
namespace Argon.Api.Features.Captcha;

using Extensions;
using Flurl.Http;
using Microsoft.Extensions.Options;

public class CloudflareCaptcha(HttpContext httpContext, ILogger<ICaptchaFeature> logger, IOptions<CaptchaOptions> options) : ICaptchaFeature
{
public async ValueTask<bool> ValidateAsync(string token)
{
if (string.IsNullOrEmpty(token))
return false;
var config = options.Value;
var remoteIp = httpContext.GetIpAddress();
try
{
var response = await config.ChallengeEndpoint
.PostMultipartAsync(content => content
.AddString("secret", config.SiteSecret)
.AddString("response", token)
.AddString("remoteip", remoteIp))
.ReceiveJson<CloudflareTurnstileResponse>();
logger.LogInformation("Success validate captcha token {Challenge_ts} {Hostname}", response.Challenge_ts, response.Hostname);
return response?.Success ?? false;
}
catch (Exception ex)
{
logger.LogCritical(ex, "failed validate captcha token");
return false;
}
}

public class CloudflareTurnstileResponse
{
public bool Success { get; set; }
public string Challenge_ts { get; set; }
public string Hostname { get; set; }
public string[] ErrorCodes { get; set; }
}

}
6 changes: 6 additions & 0 deletions src/Argon.Api/Features/Captcha/ICaptchaFeature.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Argon.Api.Features.Captcha;

public interface ICaptchaFeature
{
ValueTask<bool> ValidateAsync(string token);
}
6 changes: 6 additions & 0 deletions src/Argon.Api/Features/Captcha/NullCaptcha.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Argon.Api.Features.Captcha;

public class NullCaptcha : ICaptchaFeature
{
public ValueTask<bool> ValidateAsync(string token) => new(true);
}
6 changes: 6 additions & 0 deletions src/Argon.Api/Features/Captcha/YandexCaptcha.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Argon.Api.Features.Captcha;

public class YandexCaptcha : ICaptchaFeature
{
public ValueTask<bool> ValidateAsync(string token) => throw new NotImplementedException();
}
2 changes: 2 additions & 0 deletions src/Argon.Api/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Argon.Api.Entities;
using Argon.Api.Extensions;
using Argon.Api.Features;
using Argon.Api.Features.Captcha;
using Argon.Api.Features.EmailForms;
using Argon.Api.Features.Env;
using Argon.Api.Features.Jwt;
Expand Down Expand Up @@ -43,6 +44,7 @@
builder.AddOrleans();
builder.AddEMailForms();
builder.AddKubeResources();
builder.AddCaptchaFeature();
builder.Services.AddDataProtection();
builder.Services.AddAutoMapper(typeof(User).Assembly); // TODO
var app = builder.Build();
Expand Down
6 changes: 6 additions & 0 deletions src/Argon.Api/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,11 @@
"Orleans": {
"ClusterId": "argonapi",
"ServiceId": "argonapi"
},
"Captcha": {
"SiteKey": "",
"SiteSecret": "",
"ChallengeEndpoint": "",
"Kind": "NO_CAPTCHA"
}
}

0 comments on commit 2f46943

Please sign in to comment.