Add APP_EMAIL_ALLOWLIST gate on account creation
Build / build-and-push (push) Successful in 14s

An optional comma-separated list of glob patterns restricting which
addresses may register, applied on top of APP_ALLOW_REGISTRATION. A
non-matching new address is silently ignored exactly like registration
being off; an address that already has an account can still sign in.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-06 21:26:33 +01:00
co-authored by Claude Sonnet 5
parent 06d5b721a3
commit 8732e0e5f5
9 changed files with 120 additions and 7 deletions
+9 -5
View File
@@ -9,6 +9,7 @@ use App\Exception\ValidationException;
use App\Mail\EmailVerifier;
use App\Mail\MailException;
use App\Repository\UserRepository;
use App\Support\EmailAllowlist;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
@@ -26,6 +27,7 @@ final class AuthController extends Controller
private readonly SessionPayload $session,
private readonly EmailVerifier $verifier,
private readonly bool $allowRegistration,
private readonly EmailAllowlist $emailAllowlist,
) {
}
@@ -34,8 +36,9 @@ final class AuthController extends Controller
*
* Emails a one-time sign-in link for the given address, creating the
* account first if it doesn't exist yet -- unless registration is turned
* off (APP_ALLOW_REGISTRATION=false), in which case an unknown address is
* silently ignored and only existing users can still sign in. Always
* off (APP_ALLOW_REGISTRATION=false) or the address falls outside the
* email allowlist (APP_EMAIL_ALLOWLIST), in which case an unknown address
* is silently ignored and only existing users can still sign in. Always
* responds the same way either way, so registered addresses can't be
* enumerated. A link is only actually (re-)sent when one hasn't gone out
* in the last minute. Opening the link creates the session and, the first
@@ -50,9 +53,10 @@ final class AuthController extends Controller
throw new ValidationException(['email' => ['Enter a valid email address.']]);
}
$user = $this->allowRegistration
? $this->users->findOrCreateByEmail($email)
: $this->users->findByEmail($email);
$user = $this->users->findByEmail($email);
if ($user === null && $this->allowRegistration && $this->emailAllowlist->permits($email)) {
$user = $this->users->findOrCreateByEmail($email);
}
if ($user !== null && !$this->recentlyEmailed($user)) {
try {