From 45e6e21024e6a8491fa07f53b931263cdeac3d8b Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Sat, 27 Jun 2026 23:08:36 -0700 Subject: auth: hard-evict banned accounts at the JWT layer Add an `evicted` set check in the JWT strategy: an evicted account fails authentication on every request and every endpoint, regardless of source IP. The existing blacklist only blocks sends, so a compromised/abusive JWT could still authenticate and read/probe; eviction kills it outright. Matches on uid or username so a rename can't evade it. Co-Authored-By: Claude Opus 4.8 (1M context) --- lib/auth.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/auth.ts b/lib/auth.ts index 79c04e9d..cae73a46 100644 --- a/lib/auth.ts +++ b/lib/auth.ts @@ -1,4 +1,5 @@ import config from "$config"; +import { db } from "$lib/db"; import { fail, getUser } from "$lib/utils"; import fastifyPassport from "@fastify/passport"; import jwt from "passport-jwt"; @@ -54,6 +55,19 @@ export const jwtStrategy = new jwt.Strategy( id = id.slice(0, -3); const user = await getUser(id); + + // Hard eviction: an account in the `evicted` set cannot authenticate AT ALL + // — every request, every endpoint — regardless of source IP/VPN. Unlike the + // `blacklist` freeze (which only blocks sends), this kills the value of a + // compromised/attacker JWT outright. Match on the immutable uid OR username + // so a rename can't shake it. + if ( + user && + ((await db.sIsMember("evicted", user.id)) || + (await db.sIsMember("evicted", user.username?.toLowerCase?.().trim()))) + ) + return next(null, false); + next(null, user); }, ); -- cgit v1.2.3