Skip to content

feat: polly-based rate limiter for limiting amount of emails that could be sent for passwrod reset/email-confirm#2

Open
Fildrance wants to merge 6 commits into
Space-Wizards-Federation:masterfrom
Fildrance:feature/rate-limit-email
Open

feat: polly-based rate limiter for limiting amount of emails that could be sent for passwrod reset/email-confirm#2
Fildrance wants to merge 6 commits into
Space-Wizards-Federation:masterfrom
Fildrance:feature/rate-limit-email

Conversation

@Fildrance

@Fildrance Fildrance commented Jun 18, 2026

Copy link
Copy Markdown
Member

Improvement to email sending logic that would enable rate-limiting said process based on target email addresses.

Added polly, changed shared static class that was doing formation of emails into interface, default implementation and decorator that uses rate-limiting. EmailSendingResult is result of sending, that possibly can contain error text (if sending failed).
Changed most of code to properly use result and display error.

Rate limits are applied based on target address, as scenario with forgotten password includes situation when current (target) user is not logged in (and it is just simpler).

Change to ValueTask is dictated by advertised way of using Polly rate limiter (it does not really hurt code that was touched).

var result = await _emailSender.SendResetEmail(email, callbackUrl);
if (result.HasError)
{
return StatusCode(500, result.Error);

@Fildrance Fildrance Jun 18, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed to 429.
Usually (not normally, but usually) 429 response it returned with RetryAfter header or something like that, but sadly (and very oddly) sliding window setup from Polly does not properly give RetryAfter by itself (tested it) - i guess for now it would not be problem, as we are not aiming to get 429 properly handled by Launcher RIGHT NOW, but it will be when we will be back to it...

public sealed class EmailSender : IEmailSender
{
private readonly IRawEmailSender _rawEmailSender;
private readonly MutexDatabase _mutex;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MutexDatabase is not in use now - should i remove it? It looks kinda neat. It also is related to table for storing data - should i remove it too?

@Fildrance

Fildrance commented Jun 18, 2026

Copy link
Copy Markdown
Member Author

Tested:

  • SS14.Auth /api/auth/resetPassword (returns correct status code and plain-text body)
  • SS14.Web Identity/Account/ForgotPassword (verified - shows errors)
  • SS14.Web Identity/Account/ResendEmailConfirmation (verified - shows errors)
  • SS14.Web Admin/Users/ViewUser?id= (verified - shows errors)
  • SS14.Web Identity/Account/Manage/Email (verified - shows errors)

/// </summary>
public sealed class LimitOptions
{
public int MaxEmailsPerHour { get; set; } = 5000;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Limit name changed, as now it represents not per-hour but per-window limit - i think it should be okay?

@Fildrance Fildrance marked this pull request as ready for review June 18, 2026 18:42
@Fildrance

Copy link
Copy Markdown
Member Author

I am not very happy with caching Polly pipelines in memoryCache, they are not fat but it just does not sound pleasing (those are objects that would live quite long as a result and will stay away from being GCed). If we had Angular front-end and webapi back-end i would have just rate-limited requestes to back-end using middleware, but with razor - it won't be looking good, i bet...
In the end i removed separate cache-keys for confirm and reset emails, does not seem to worth it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant