Budget registration per address; embed difficulty; expiry queue; 404 on empty withdraw

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Heimdall 2026-11-11 09:34:44 +00:00
commit e67257c750
11 changed files with 368 additions and 36 deletions

View file

@ -134,6 +134,7 @@ builder.Services.AddOptions<MindIntakeOptions>().BindConfiguration(MindIntakeOpt
builder.Services.AddSingleton<MindIntakeRateLimit>(); // TimeProvider and options resolve from the container
builder.Services.AddOptions<RegistrationOptions>().BindConfiguration(RegistrationOptions.SectionName);
builder.Services.AddSingleton<RegistrationChallengeService>();
builder.Services.AddSingleton<RegistrationRateLimit>();
builder.Services.AddHostedService<SealedBallotCloser>();
builder.Services.AddScoped<MotionEveSurfaceService>();
builder.Services.AddScoped<PatronageService>();
@ -390,8 +391,14 @@ app.MapPost("/auth/bifrost/register", async (
HttpContext httpContext,
BifrostIdentityService bifrostIdentityService,
RegistrationChallengeService challenges,
RegistrationRateLimit rateLimit,
CancellationToken cancellationToken) =>
{
if (rateLimit.Refuse(httpContext) is { } refused)
{
return refused;
}
// Consent first, then the proof of work; a refusal here creates nothing.
if (request.AcceptedNoticeVersion != TrainingNotice.Version)
{
@ -432,8 +439,13 @@ app.MapPost("/auth/bifrost/register", async (
}
}).AllowAnonymous();
app.MapGet("/auth/bifrost/challenge", (HttpContext httpContext, RegistrationChallengeService challenges) =>
app.MapGet("/auth/bifrost/challenge", (HttpContext httpContext, RegistrationChallengeService challenges, RegistrationRateLimit rateLimit) =>
{
if (rateLimit.Refuse(httpContext) is { } refused)
{
return refused;
}
httpContext.Response.Headers.CacheControl = "no-store";
var challenge = challenges.Issue();
return challenge is null
@ -460,6 +472,11 @@ app.MapPost("/auth/bifrost/consent/withdraw", async (
var receipts = await dbContext.ConsentReceipts
.Where(x => x.UserAccountId == actor.UserAccount.Id)
.ToListAsync(cancellationToken);
if (receipts.Count == 0)
{
return Results.Text("No consent receipt is recorded for this account.", statusCode: StatusCodes.Status404NotFound);
}
var now = clock.GetUtcNow();
foreach (var receipt in receipts.Where(x => x.WithdrawnAtUtc is null))
{