feat(server): anonymous sign-in

People can sign in without an account: AnonymousIdpEndpoint exposes Serverpod's anonymous identity provider. lib/src/auth/auth_config.dart holds the token managers and identity providers, shared by server.dart and the tests, so the tests check what production runs. At most 30 new anonymous accounts per IP address and hour (Serverpod's default is 100): a school class behind one address can still start together, while one abuser gets at most 720 a day. No migration: the tables for anonymous accounts and rate limiting already exist.

Tests: signing in creates a person; the 31st account from one IP address within an hour is refused. They run without database rollback, since signing in uses concurrent transactions that the rollback can't wrap, and delete what they create instead.

The server switches off the lint rules for Dart v3.13's constructor syntax: Serverpod v4.0.2's code generator can't parse it yet.

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

View file

@ -10,6 +10,7 @@
// ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'dart:async' as _ida;
import 'package:http/http.dart' as _i85jenna;
import 'package:serverpod_auth_core_client/serverpod_auth_core_client.dart'
as _iacc;
@ -18,6 +19,28 @@ import 'package:serverpod_auth_idp_client/serverpod_auth_idp_client.dart'
import 'package:serverpod_client/serverpod_client.dart' as _isc;
import 'protocol.dart' as _il2as5qe;
/// Signs people in anonymously; the app calls it on first launch. Creating an
/// account is rate-limited per IP address (see `auth_config.dart`).
/// {@category Endpoint}
class EndpointAnonymousIdp extends _iaic.EndpointAnonymousIdpBase {
EndpointAnonymousIdp(_isc.EndpointCaller caller) : super(caller);
@override
String get name => '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.
@override
_ida.Future<_iacc.AuthSuccess> login({String? token}) =>
caller.callServerEndpoint<_iacc.AuthSuccess>(
'anonymousIdp',
'login',
{'token': token},
);
}
class Modules {
Modules(Client client) {
serverpod_auth_idp = _iaic.Caller(client);
@ -56,13 +79,18 @@ class Client extends _isc.ServerpodClientShared {
disconnectStreamsOnLostInternetConnection,
httpClientOverride: httpClientOverride,
) {
anonymousIdp = EndpointAnonymousIdp(this);
modules = Modules(this);
}
late final EndpointAnonymousIdp anonymousIdp;
late final Modules modules;
@override
Map<String, _isc.EndpointRef> get endpointRefLookup => {};
Map<String, _isc.EndpointRef> get endpointRefLookup => {
'anonymousIdp': anonymousIdp,
};
@override
Map<String, _isc.ModuleEndpointCaller> get moduleLookup => {