fix(presence): drop status='online' from registration insert

Mirrors the REST-login change in f17c46c on the registration insert path.
A successful POST /api/auth/register does not by itself imply a live
WebSocket — the client may never connect (transient network, mobile
background, error path between the 201 and /ws), leaving a permanently
stuck-online row that no disconnect timer can clean up. The schema
default 'offline' is correct; ws/handler.ts flips it to 'online' on real
WS auth.

The federated-stub upgrade path in the same handler is unaffected: it
only updates passwordHash/username/homeUserId/displayName/avatarColor,
leaving the stub's pre-existing 'offline' status (set when the stub was
created via replication) untouched.

Updates docs/systems/auth.md step 7 to reflect the new behavior.
This commit is contained in:
Jannis Braun
2026-04-27 09:35:34 +02:00
parent f17c46c77f
commit 4fa2414933
2 changed files with 9 additions and 2 deletions
+1 -1
View File
@@ -149,7 +149,7 @@ If `requestedAvatarColor` is provided and is in `AVATAR_COLORS`, use it. Otherwi
4. Check username uniqueness (exact match on lowercased username) 4. Check username uniqueness (exact match on lowercased username)
5. Hash password (bcrypt, 12 rounds) 5. Hash password (bcrypt, 12 rounds)
6. Generate Snowflake ID 6. Generate Snowflake ID
7. Insert user row with `status: 'online'`, admin flag if first user 7. Insert user row (status defaults to `'offline'` at the schema level; it is set to `'online'` only when the client establishes a WebSocket via the WS auth path in `ws/handler.ts`). Admin flag set if first user.
8. Sign JWT with `{ userId, username }` 8. Sign JWT with `{ userId, username }`
9. Return `{ token, user }` (user sanitized via `sanitizeUser(user, true)`) 9. Return `{ token, user }` (user sanitized via `sanitizeUser(user, true)`)
+8 -1
View File
@@ -166,12 +166,19 @@ export async function authRoutes(app: FastifyInstance): Promise<void> {
? requestedAvatarColor ? requestedAvatarColor
: AVATAR_COLORS[Math.floor(Math.random() * AVATAR_COLORS.length)]; : AVATAR_COLORS[Math.floor(Math.random() * AVATAR_COLORS.length)];
// Note: status is left at the schema default ('offline') and is set to
// 'online' exclusively by the WebSocket auth path (ws/handler.ts). A
// successful REST /register does not by itself imply a live connection —
// the client may never establish a WS (transient network failure, mobile
// background, error path between this 201 response and /ws connect),
// which would otherwise produce a permanently stuck-online row that no
// disconnect timer can clean up. The WS handshake will flip it to
// 'online' once a real socket attaches.
db.insert(schema.users).values({ db.insert(schema.users).values({
id: userId, id: userId,
username: trimmedUsername, username: trimmedUsername,
displayName: displayName?.trim() || null, displayName: displayName?.trim() || null,
passwordHash, passwordHash,
status: 'online',
isAdmin: isFirstUser ? 1 : 0, isAdmin: isFirstUser ? 1 : 0,
homeInstance: homeInstance || null, homeInstance: homeInstance || null,
homeUserId: (homeInstance && homeUserId && typeof homeUserId === 'string') ? homeUserId : null, homeUserId: (homeInstance && homeUserId && typeof homeUserId === 'string') ? homeUserId : null,