Use old session id from old token for refresh. Refresh offset set to ten minutes.

This commit is contained in:
Hendrik Belitz
2025-09-08 11:31:36 +02:00
parent f737ab8b40
commit 326db3863f
3 changed files with 33 additions and 7 deletions
+23 -1
View File
@@ -1,4 +1,4 @@
import { SignJWT, jwtVerify, type JWTPayload } from "jose";
import { SignJWT, decodeJwt, jwtVerify, type JWTPayload } from "jose";
import { env } from "$env/dynamic/private";
import type { SelectUser } from "$lib/server/db/central-schema";
import { UniversalLogger } from "$lib/logger";
@@ -57,6 +57,28 @@ export async function generateRefreshToken(userId: string, sessionId: string): P
return jwt;
}
export async function decodeAccessToken(
token: string,
): Promise<(JWTPayload & { userId: string; sessionId: string }) | null> {
try {
const payload = await decodeJwt(token);
return {
userId: payload.userId as string,
email: payload.email,
name: payload.name,
role: payload.role as "GLOBAL_ADMIN" | "TENANT_ADMIN" | "STAFF",
tenantId: payload.tenantId as string | undefined,
sessionId: payload.sessionId as string,
iat: payload.iat,
exp: payload.exp,
};
} catch (error) {
logger.warn("JWT verification failed:", { error: String(error) });
return null;
}
}
export async function verifyAccessToken(
token: string,
): Promise<(JWTPayload & { userId: string; sessionId: string }) | null> {
+3 -3
View File
@@ -285,13 +285,13 @@ export class SessionService {
logger.info("Expired sessions cleaned up");
}
static async getUserSession(accessToken: string): Promise<SelectUserSession | null> {
logger.debug(`Getting active sessions for a given access token`);
static async getUserSession(sessionId: string): Promise<SelectUserSession | null> {
logger.debug(`Getting active sessions for session id: ${sessionId}`);
const sessions = await db
.select()
.from(userSession)
.where(eq(userSession.accessToken, accessToken))
.where(eq(userSession.id, sessionId))
.orderBy(userSession.lastUsedAt);
return sessions[0] ?? null;
+7 -3
View File
@@ -3,9 +3,11 @@ import { SessionService } from "$lib/server/auth/session-service";
import type { RequestHandler } from "./$types";
import { registerOpenAPIRoute } from "$lib/server/openapi";
import { UniversalLogger } from "$lib/logger";
import { isBefore } from "date-fns";
import { isBefore, subMinutes } from "date-fns";
import { decodeAccessToken } from "$lib/server/auth/jwt-utils";
const logger = new UniversalLogger().setContext("AuthRefreshAPI");
const REFRESH_OFFSET = 10;
registerOpenAPIRoute("/auth/refresh", "POST", {
summary: "Refresh access token",
@@ -68,13 +70,15 @@ export const POST: RequestHandler = async ({ cookies }) => {
}
// Get the (possibly invalid) session from the database
const oldSession = await SessionService.getUserSession(accessToken);
const decodedToken = await decodeAccessToken(accessToken);
const oldSessionId = decodedToken?.sessionId ?? null;
const oldSession = oldSessionId ? await SessionService.getUserSession(oldSessionId) : null;
if (!oldSession) {
return json({ error: "No valid session found for user" }, { status: 401 });
}
if (isBefore(oldSession.expiresAt, new Date())) {
if (isBefore(oldSession.expiresAt, subMinutes(new Date(), REFRESH_OFFSET))) {
return json(
{
message: "Session still valid, no refresh needed",