"use server"; import { and, eq, sql } from "drizzle-orm"; import { auth, signOut } from "@/lib/auth"; import { invalidateJwtVersionCache } from "@/lib/auth/jwt-version-cache"; import { personalTokenScope } from "@/lib/auth/personal-token-scope"; import { db, PersonalAccessTokens, User } from "@/lib/db"; import { logger } from "@/lib/logger"; /** * Invalidate every CMS JWT for the signed-in user by bumping website_jwt_version, * revoke personal access tokens, then end the current browser session too. */ export async function signOutEverywhere(): Promise { const session = await auth(); const userId = Number(session?.user?.id); if (!Number.isInteger(userId) || userId <= 0) { await signOut({ redirectTo: "/login" }); return; } try { await db .update(User) .set({ websiteJwtVersion: sql`${User.websiteJwtVersion} + 1`, }) .where(eq(User.id, userId)); await invalidateJwtVersionCache(userId); } catch (err) { logger.warn("Failed to bump JWT version during sign-out-everywhere", { userId, error: err instanceof Error ? err.message : "Unknown", }); } // Revoke API bearer tokens (Sanctum / personal_access_tokens). try { const scope = personalTokenScope(userId); await db .delete(PersonalAccessTokens) .where( and( eq(PersonalAccessTokens.tokenableId, scope.tokenableId), eq(PersonalAccessTokens.tokenableType, scope.tokenableType), ), ); } catch (err) { logger.warn( "Failed to revoke personal access tokens during sign-out-everywhere", { userId, error: err instanceof Error ? err.message : "Unknown", }, ); } await signOut({ redirectTo: "/login?signedOutAll=1" }); }