fix(auth): count per-network limits by the visitor behind Caddy

Every per-network limit counted by the connection's address, which behind
Caddy is Caddy's for everyone: new anonymous accounts, codes by mail and
password sign-ins were each limited for the whole instance.

`networkOf` (now `common/network.dart`) takes the last address of
X-Forwarded-For, the one the proxy saw itself, for a connection from a
proxy the operator trusts (`TRUSTED_PROXIES`, which deploy/compose.yaml
sets to Caddy's fixed address); otherwise, or where that is no address,
the connection's. Never the Forwarded header, which Caddy passes on as
sent, so never Serverpod's `remoteInfo`. A value that isn't addresses
stops the server as it starts.

Serverpod's own limit on anonymous accounts counts by the connection's
address too and can't be told otherwise, so it is off; the anonymous
endpoint counts them itself, 30 an hour per network as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-20 14:09:27 +02:00
commit d9039d36ef
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -74,8 +74,7 @@ class EndpointAccount extends _isc.EndpointRef {
);
}
/// Signs people in anonymously; the app calls it on first launch. Creating an
/// account is rate-limited per IP address (see `auth_config.dart`).
/// Signs people in anonymously; the app calls it on first launch.
/// {@category Endpoint}
class EndpointAnonymousIdp extends _iaic.EndpointAnonymousIdpBase {
EndpointAnonymousIdp(_isc.EndpointCaller caller) : super(caller);
@ -83,10 +82,11 @@ class EndpointAnonymousIdp extends _iaic.EndpointAnonymousIdpBase {
@override
String get name => 'halkyon_base.anonymousIdp';
/// Creates a new anonymous account and returns its session.
///
/// Invokes the [AnonymousIdp.beforeAnonymousAccount] callback if configured,
/// which may prevent account creation if the endpoint is protected.
/// Creates a new anonymous account and returns its session: at most 30 an
/// hour from one network (`anonymousSignUpLimit`), counted by the
/// visitor's address (`networkOf`) rather than as Serverpod's own limit
/// counts them, by the connection's, which behind a proxy is the proxy's
/// for everyone.
@override
_ida.Future<_iacc.AuthSuccess> login({String? token}) =>
caller.callServerEndpoint<_iacc.AuthSuccess>(