✨ Recaptcha
This commit is contained in:
parent
31db3d5388
commit
a008a74d77
@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Mvc;
|
|||||||
using NodaTime;
|
using NodaTime;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System.IdentityModel.Tokens.Jwt;
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace DysonNetwork.Sphere.Auth;
|
namespace DysonNetwork.Sphere.Auth;
|
||||||
|
|
||||||
@ -14,7 +15,8 @@ public class AuthController(
|
|||||||
AppDatabase db,
|
AppDatabase db,
|
||||||
AccountService accounts,
|
AccountService accounts,
|
||||||
AuthService auth,
|
AuthService auth,
|
||||||
IHttpContextAccessor httpContext
|
IConfiguration configuration,
|
||||||
|
IHttpClientFactory httpClientFactory
|
||||||
) : ControllerBase
|
) : ControllerBase
|
||||||
{
|
{
|
||||||
public class ChallengeRequest
|
public class ChallengeRequest
|
||||||
@ -31,8 +33,8 @@ public class AuthController(
|
|||||||
var account = await accounts.LookupAccount(request.Account);
|
var account = await accounts.LookupAccount(request.Account);
|
||||||
if (account is null) return NotFound("Account was not found.");
|
if (account is null) return NotFound("Account was not found.");
|
||||||
|
|
||||||
var ipAddress = httpContext.HttpContext?.Connection.RemoteIpAddress?.ToString();
|
var ipAddress = HttpContext.Connection.RemoteIpAddress?.ToString();
|
||||||
var userAgent = httpContext.HttpContext?.Request.Headers.UserAgent.ToString();
|
var userAgent = HttpContext.Request.Headers.UserAgent.ToString();
|
||||||
|
|
||||||
var now = Instant.FromDateTimeUtc(DateTime.UtcNow);
|
var now = Instant.FromDateTimeUtc(DateTime.UtcNow);
|
||||||
|
|
||||||
@ -186,7 +188,7 @@ public class AuthController(
|
|||||||
[HttpGet("test")]
|
[HttpGet("test")]
|
||||||
public async Task<ActionResult> Test()
|
public async Task<ActionResult> Test()
|
||||||
{
|
{
|
||||||
var sessionIdClaim = httpContext.HttpContext?.User.FindFirst("session_id")?.Value;
|
var sessionIdClaim = HttpContext.User.FindFirst("session_id")?.Value;
|
||||||
if (!Guid.TryParse(sessionIdClaim, out var sessionId))
|
if (!Guid.TryParse(sessionIdClaim, out var sessionId))
|
||||||
return Unauthorized();
|
return Unauthorized();
|
||||||
|
|
||||||
@ -195,4 +197,53 @@ public class AuthController(
|
|||||||
|
|
||||||
return Ok(session);
|
return Ok(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost("captcha")]
|
||||||
|
public async Task<ActionResult> ValidateCaptcha([FromBody] string token)
|
||||||
|
{
|
||||||
|
var provider = configuration.GetSection("Captcha")["Provider"]?.ToLower();
|
||||||
|
var apiKey = configuration.GetSection("Captcha")["ApiKey"];
|
||||||
|
var apiSecret = configuration.GetSection("Captcha")["ApiSecret"];
|
||||||
|
|
||||||
|
var client = httpClientFactory.CreateClient();
|
||||||
|
|
||||||
|
switch (provider)
|
||||||
|
{
|
||||||
|
case "cloudflare":
|
||||||
|
var content = new StringContent($"secret={apiSecret}&response={token}", System.Text.Encoding.UTF8,
|
||||||
|
"application/x-www-form-urlencoded");
|
||||||
|
var response = await client.PostAsync("https://challenges.cloudflare.com/turnstile/v0/siteverify",
|
||||||
|
content);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
|
var json = await response.Content.ReadAsStringAsync();
|
||||||
|
var cfResult = JsonSerializer.Deserialize<CloudflareVerificationResponse>(json);
|
||||||
|
|
||||||
|
if (cfResult?.Success == true)
|
||||||
|
return Ok(new { success = true });
|
||||||
|
|
||||||
|
return BadRequest(new { success = false, errors = cfResult?.ErrorCodes });
|
||||||
|
case "google":
|
||||||
|
var secretKey = configuration.GetSection("CaptchaSettings")["GoogleRecaptchaSecretKey"];
|
||||||
|
if (string.IsNullOrEmpty(secretKey))
|
||||||
|
{
|
||||||
|
return StatusCode(500, "Google reCaptcha secret key is not configured.");
|
||||||
|
}
|
||||||
|
|
||||||
|
content = new StringContent($"secret={secretKey}&response={token}", System.Text.Encoding.UTF8,
|
||||||
|
"application/x-www-form-urlencoded");
|
||||||
|
response = await client.PostAsync("https://www.google.com/recaptcha/api/siteverify", content);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
|
json = await response.Content.ReadAsStringAsync();
|
||||||
|
var capResult = JsonSerializer.Deserialize<GoogleVerificationResponse>(json);
|
||||||
|
|
||||||
|
if (capResult?.Success == true)
|
||||||
|
return Ok(new { success = true });
|
||||||
|
|
||||||
|
return BadRequest(new { success = false, errors = capResult?.ErrorCodes });
|
||||||
|
default:
|
||||||
|
return StatusCode(500, "The server misconfigured for the captcha.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
17
DysonNetwork.Sphere/Auth/CheckpointModel.cs
Normal file
17
DysonNetwork.Sphere/Auth/CheckpointModel.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
namespace DysonNetwork.Sphere.Auth;
|
||||||
|
|
||||||
|
public class CloudflareVerificationResponse
|
||||||
|
{
|
||||||
|
public bool Success { get; set; }
|
||||||
|
public string[]? ErrorCodes { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GoogleVerificationResponse
|
||||||
|
{
|
||||||
|
public bool Success { get; set; }
|
||||||
|
public float Score { get; set; }
|
||||||
|
public string Action { get; set; }
|
||||||
|
public DateTime ChallengeTs { get; set; }
|
||||||
|
public string Hostname { get; set; }
|
||||||
|
public string[]? ErrorCodes { get; set; }
|
||||||
|
}
|
149
DysonNetwork.Sphere/Pages/CheckpointPage.cshtml
Normal file
149
DysonNetwork.Sphere/Pages/CheckpointPage.cshtml
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
@page "/auth/captcha"
|
||||||
|
@model DysonNetwork.Sphere.Pages.CheckpointPage
|
||||||
|
|
||||||
|
@{
|
||||||
|
Layout = null;
|
||||||
|
|
||||||
|
var cfg = ViewData.Model.Configuration;
|
||||||
|
var provider = cfg.GetSection("Captcha")["Provider"]?.ToLower();
|
||||||
|
var apiKey = cfg.GetSection("Captcha")["ApiKey"];
|
||||||
|
}
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8"/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
|
<title>Solar Network Captcha</title>
|
||||||
|
<link
|
||||||
|
href="https://fonts.googleapis.com/css2?family=Roboto+Mono&display=swap"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100vh;
|
||||||
|
background-color: #2d2d2d;
|
||||||
|
font-family: "Roboto Mono", monospace;
|
||||||
|
color: #c9d1d9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.parent {
|
||||||
|
padding: 20px;
|
||||||
|
max-width: 480px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 300;
|
||||||
|
color: #ffffff;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
margin-top: 20px;
|
||||||
|
font-size: 11px;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-product {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.g-recaptcha {
|
||||||
|
display: inline-block; /* Adjust as needed */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@switch (provider)
|
||||||
|
{
|
||||||
|
case "recaptcha":
|
||||||
|
<script src="https://www.recaptcha.net/recaptcha/api.js" async defer></script>
|
||||||
|
break;
|
||||||
|
case "cloudflare":
|
||||||
|
<script
|
||||||
|
src="https://challenges.cloudflare.com/turnstile/v0/api.js"
|
||||||
|
async
|
||||||
|
defer
|
||||||
|
></script>
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="parent">
|
||||||
|
<div class="container">
|
||||||
|
<h1>reCaptcha</h1>
|
||||||
|
@switch (provider)
|
||||||
|
{
|
||||||
|
case "cloudflare":
|
||||||
|
<div
|
||||||
|
class="cf-turnstile"
|
||||||
|
data-sitekey="@apiKey"
|
||||||
|
data-callback="onSuccess"
|
||||||
|
></div>
|
||||||
|
break;
|
||||||
|
case "recaptcha":
|
||||||
|
<div
|
||||||
|
class="g-recaptcha"
|
||||||
|
data-sitekey="@apiKey"
|
||||||
|
data-callback="onSuccess"
|
||||||
|
></div>
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
<p style="color: yellow;">Captcha provider not configured correctly.</p>
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div class="footer">
|
||||||
|
<div class="footer-product">Solar Network Anti-Robot</div>
|
||||||
|
<a
|
||||||
|
href="https://solsynth.dev"
|
||||||
|
style="color: #c9d1d9; text-decoration: none"
|
||||||
|
>Solsynth LLC</a>
|
||||||
|
© @DateTime.Now.Year<br/>
|
||||||
|
Powered by
|
||||||
|
@switch (provider)
|
||||||
|
{
|
||||||
|
case "cloudflare":
|
||||||
|
<a href="https://www.cloudflare.com/turnstile/" style="color: #c9d1d9"
|
||||||
|
>Cloudflare Turnstile</a>
|
||||||
|
break;
|
||||||
|
case "recaptcha":
|
||||||
|
<a href="https://www.google.com/recaptcha/" style="color: #c9d1d9"
|
||||||
|
>Google reCaptcha</a>
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
<span>Nothing</span>
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
<br/>
|
||||||
|
Hosted by
|
||||||
|
<a
|
||||||
|
@* TODO Update the project link here *@
|
||||||
|
href="https://github.com/Solsynth/HyperNet.Nexus"
|
||||||
|
style="color: #c9d1d9"
|
||||||
|
>DysonNetwork.Sphere</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
function getQueryParam(name) {
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
return urlParams.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSuccess(token) {
|
||||||
|
window.parent.postMessage("captcha_tk=" + token, "*");
|
||||||
|
const redirectUri = getQueryParam("redirect_uri");
|
||||||
|
if (redirectUri) {
|
||||||
|
window.location.href = `${redirectUri}?captcha_tk=${encodeURIComponent(token)}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
13
DysonNetwork.Sphere/Pages/CheckpointPage.cshtml.cs
Normal file
13
DysonNetwork.Sphere/Pages/CheckpointPage.cshtml.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
|
||||||
|
namespace DysonNetwork.Sphere.Pages;
|
||||||
|
|
||||||
|
public class CheckpointPage(IConfiguration configuration) : PageModel
|
||||||
|
{
|
||||||
|
[BindProperty] public IConfiguration Configuration { get; set; } = configuration;
|
||||||
|
|
||||||
|
public void OnGet()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@ -40,7 +40,7 @@ builder.Services.AddControllers().AddJsonOptions(options =>
|
|||||||
|
|
||||||
options.JsonSerializerOptions.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
|
options.JsonSerializerOptions.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
|
||||||
});
|
});
|
||||||
builder.Services.AddHttpContextAccessor();
|
builder.Services.AddRazorPages();
|
||||||
|
|
||||||
// Casbin permissions
|
// Casbin permissions
|
||||||
|
|
||||||
@ -155,7 +155,7 @@ using (var scope = app.Services.CreateScope())
|
|||||||
db.Database.Migrate();
|
db.Database.Migrate();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (app.Environment.IsDevelopment()) app.MapOpenApi();
|
app.MapOpenApi();
|
||||||
|
|
||||||
app.UseSwagger();
|
app.UseSwagger();
|
||||||
app.UseSwaggerUI();
|
app.UseSwaggerUI();
|
||||||
@ -179,6 +179,8 @@ app.UseAuthorization();
|
|||||||
app.UseMiddleware<UserInfoMiddleware>();
|
app.UseMiddleware<UserInfoMiddleware>();
|
||||||
|
|
||||||
app.MapControllers();
|
app.MapControllers();
|
||||||
|
app.MapStaticAssets();
|
||||||
|
app.MapRazorPages();
|
||||||
|
|
||||||
var tusDiskStore = new tusdotnet.Stores.TusDiskStore(
|
var tusDiskStore = new tusdotnet.Stores.TusDiskStore(
|
||||||
builder.Configuration.GetSection("Tus").GetValue<string>("StorePath")!
|
builder.Configuration.GetSection("Tus").GetValue<string>("StorePath")!
|
||||||
|
@ -44,5 +44,10 @@
|
|||||||
"EnableSsl": true
|
"EnableSsl": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"Captcha": {
|
||||||
|
"Provider": "recaptcha",
|
||||||
|
"ApiKey": "6LfIzSArAAAAAN413MtycDcPlKa636knBSAhbzj-",
|
||||||
|
"ApiSecret": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user