60 lines
1.6 KiB
TypeScript
60 lines
1.6 KiB
TypeScript
"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<void> {
|
|
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" });
|
|
}
|