diff --git a/src/lib/server/auth/__tests__/jwt-utils.test.ts b/src/lib/server/auth/__tests__/jwt-utils.test.ts index b8d98ba..74f88b1 100644 --- a/src/lib/server/auth/__tests__/jwt-utils.test.ts +++ b/src/lib/server/auth/__tests__/jwt-utils.test.ts @@ -21,7 +21,9 @@ const mockUser: SelectUser = { isActive: true, confirmed: true, token: null, - tokenValidUntil: null + tokenValidUntil: null, + passphraseHash: null, + recoveryPassphrase: null }; describe("JWT Utils", () => { diff --git a/src/routes/api/admin/tenant/+server.ts b/src/routes/api/admin/tenant/+server.ts index 970e0fd..91a6630 100644 --- a/src/routes/api/admin/tenant/+server.ts +++ b/src/routes/api/admin/tenant/+server.ts @@ -1,12 +1,11 @@ import { error, json, type RequestHandler } from "@sveltejs/kit"; import { z } from "zod"; -import { SessionService } from "$lib/server/auth/session-service"; import { centralDb } from "$lib/server/db"; -import { tenant, user, userSession } from "$lib/server/db/central-schema"; +import { tenant, user } from "$lib/server/db/central-schema"; import { eq } from "drizzle-orm"; import { ValidationError, NotFoundError } from "$lib/server/utils/errors"; import { UserService } from "$lib/server/services/user-service"; -import { generateTokens } from "$lib/server/auth/jwt-utils"; +import { generateAccessToken } from "$lib/server/auth/jwt-utils"; import { UniversalLogger } from "$lib/logger"; import { registerOpenAPIRoute } from "$lib/server/openapi"; @@ -59,86 +58,26 @@ export const POST: RequestHandler = async ({ request, locals, cookies }) => { throw error(404, "Tenant not found"); } - // Get current session token from cookie - const sessionToken = cookies.get("session"); - if (!sessionToken) { - logger.error("No session token found in cookies", { userId: locals.user.id }); - throw error(401, "No valid session found"); - } - - // Get current session data - const sessionData = await SessionService.validateSession(sessionToken); - if (!sessionData) { - logger.error("Invalid session token", { userId: locals.user.id }); - throw error(401, "Invalid session"); - } + // Current user is already authenticated via authHandle // Update user's active tenant in the database - const updatedUser = await UserService.updateUser(locals.user.id, { + const updatedUser = await UserService.updateUser(locals.user.userId as string, { tenantId: tenantId || null }); if (!updatedUser) { logger.error("Failed to update user tenant", { - userId: locals.user.id, + userId: locals.user.userId, tenantId }); throw error(500, "Failed to update user data"); } - // Get the session ID from the database - const sessionInfo = await centralDb - .select({ id: userSession.id }) - .from(userSession) - .where(eq(userSession.sessionToken, sessionData.sessionToken)) - .limit(1); + // Generate new access token with updated tenant context + const newAccessToken = await generateAccessToken(updatedUser, (locals.user.sessionId as string) || "temp-session"); - if (sessionInfo.length === 0) { - logger.error("Session not found in database", { userId: locals.user.id }); - throw error(500, "Session not found"); - } - - // Generate new tokens with updated tenant context - const newTokens = await generateTokens(updatedUser, sessionInfo[0].id); - - // Update session tokens in the database - await centralDb - .update(user) - .set({ - tenantId: tenantId || null, - updatedAt: new Date() - }) - .where(eq(user.id, locals.user.id)); - - // Update the user session with new tokens - await centralDb - .update(userSession) - .set({ - accessToken: newTokens.accessToken, - refreshToken: newTokens.refreshToken, - lastUsedAt: new Date(), - updatedAt: new Date() - }) - .where(eq(userSession.sessionToken, sessionData.sessionToken)); - - // Set new cookies with updated tokens - cookies.set("session", sessionData.sessionToken, { - httpOnly: true, - secure: true, - sameSite: "strict", - path: "/", - maxAge: 60 * 60 * 24 * 7 // 7 days - }); - - cookies.set("accessToken", newTokens.accessToken, { - httpOnly: true, - secure: true, - sameSite: "strict", - path: "/", - maxAge: 60 * 15 // 15 minutes - }); - - cookies.set("refreshToken", newTokens.refreshToken, { + // Set new access token cookie + cookies.set("access_token", newAccessToken, { httpOnly: true, secure: true, sameSite: "strict", @@ -147,7 +86,7 @@ export const POST: RequestHandler = async ({ request, locals, cookies }) => { }); logger.info("Tenant switched successfully", { - userId: locals.user.id, + userId: locals.user.userId, fromTenant: locals.user.tenantId, toTenant: tenantId }); diff --git a/src/routes/api/admin/tenant/server.test.ts b/src/routes/api/admin/tenant/server.test.ts index 1aaf6ca..fcf4c4e 100644 --- a/src/routes/api/admin/tenant/server.test.ts +++ b/src/routes/api/admin/tenant/server.test.ts @@ -53,7 +53,7 @@ vi.mock("$lib/server/services/user-service", () => ({ // Mock JWT utils vi.mock("$lib/server/auth/jwt-utils", () => ({ - generateTokens: vi.fn() + generateAccessToken: vi.fn() })); // Mock universal logger @@ -92,7 +92,7 @@ vi.mock("$lib/server/utils/errors", () => ({ import { SessionService } from "$lib/server/auth/session-service"; import { centralDb } from "$lib/server/db"; import { UserService } from "$lib/server/services/user-service"; -import { generateTokens } from "$lib/server/auth/jwt-utils"; +import { generateAccessToken } from "$lib/server/auth/jwt-utils"; describe("POST /api/admin/tenant", () => { const mockUser = { @@ -107,7 +107,9 @@ describe("POST /api/admin/tenant", () => { updatedAt: new Date(), lastLoginAt: null, token: null, - tokenValidUntil: null + tokenValidUntil: null, + passphraseHash: null, + recoveryPassphrase: null }; const mockSessionData = { @@ -130,7 +132,7 @@ describe("POST /api/admin/tenant", () => { request: { json: () => Promise.resolve(body) } as Request, - locals: { user }, + locals: { user: { ...user, sessionId: "session-123" } }, cookies: mockCookies, params: {}, url: new URL("http://localhost/api/admin/tenant"), @@ -159,25 +161,14 @@ describe("POST /api/admin/tenant", () => { }; vi.mocked(centralDb.select).mockReturnValue(mockSelectQuery as any); - // Mock session cookie - mockCookies.get.mockReturnValue("session-123"); - - // Mock session validation - const mockValidateSession = vi.mocked(SessionService.validateSession); - mockValidateSession.mockResolvedValue(mockSessionData as any); - - // Mock session ID lookup - mockSelectQuery.limit.mockResolvedValueOnce([{ id: "session-id-123" }]); + // No session handling needed anymore // Mock user update const updatedUser = { ...mockUser, tenantId }; vi.mocked(UserService.updateUser).mockResolvedValue(updatedUser as any); // Mock token generation - vi.mocked(generateTokens).mockResolvedValue({ - accessToken: "new-access-123", - refreshToken: "new-refresh-123" - }); + vi.mocked(generateAccessToken).mockResolvedValue("new-access-123"); // Mock database update queries const mockUpdateQuery = { @@ -193,17 +184,11 @@ describe("POST /api/admin/tenant", () => { expect(result.success).toBe(true); expect(result.tenantId).toBe(tenantId); expect(result.user.tenantId).toBe(tenantId); - expect(mockCookies.set).toHaveBeenCalledWith("session", "session-123", expect.any(Object)); expect(mockCookies.set).toHaveBeenCalledWith( - "accessToken", + "access_token", "new-access-123", expect.any(Object) ); - expect(mockCookies.set).toHaveBeenCalledWith( - "refreshToken", - "new-refresh-123", - expect.any(Object) - ); }); it("should return 401 when user is not authenticated", async () => { diff --git a/src/routes/api/auth/login/+server.ts b/src/routes/api/auth/login/+server.ts index ab44274..ad7c93c 100644 --- a/src/routes/api/auth/login/+server.ts +++ b/src/routes/api/auth/login/+server.ts @@ -86,14 +86,13 @@ registerOpenAPIRoute("/auth/login", "POST", { }, required: ["id", "email", "name", "role"] }, - accessToken: { type: "string", description: "JWT access token" }, expiresAt: { type: "string", format: "date-time", description: "Session expiration time" } }, - required: ["message", "user", "accessToken", "expiresAt"] + required: ["message", "user", "expiresAt"] } } } @@ -273,8 +272,8 @@ export const POST: RequestHandler = async ({ request, cookies, getClientAddress, userAgent || undefined ); - // Set HTTP-only cookie for session - cookies.set("session", sessionData.sessionToken, { + // Set HTTP-only cookie for access token + cookies.set("access_token", sessionData.accessToken, { httpOnly: true, secure: true, sameSite: "strict", @@ -298,7 +297,6 @@ export const POST: RequestHandler = async ({ request, cookies, getClientAddress, role: sessionData.user.role, tenantId: sessionData.user.tenantId }, - accessToken: sessionData.accessToken, expiresAt: sessionData.expiresAt.toISOString() }); } catch (error) { diff --git a/src/routes/api/auth/logout/+server.ts b/src/routes/api/auth/logout/+server.ts index 58cd134..d1a511e 100644 --- a/src/routes/api/auth/logout/+server.ts +++ b/src/routes/api/auth/logout/+server.ts @@ -1,5 +1,4 @@ import { json } from "@sveltejs/kit"; -import { SessionService } from "$lib/server/auth/session-service"; import type { RequestHandler } from "./$types"; import { registerOpenAPIRoute } from "$lib/server/openapi"; import { UniversalLogger } from "$lib/logger"; @@ -8,7 +7,7 @@ const logger = new UniversalLogger().setContext("AuthLogoutAPI"); registerOpenAPIRoute("/auth/logout", "POST", { summary: "Logout user session", - description: "Invalidate current user session and clear session cookie", + description: "Invalidate current user session and clear access token cookie", tags: ["Authentication"], responses: { "200": { @@ -26,7 +25,7 @@ registerOpenAPIRoute("/auth/logout", "POST", { } }, "400": { - description: "No active session found", + description: "No access token found", content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } @@ -46,15 +45,14 @@ registerOpenAPIRoute("/auth/logout", "POST", { export const POST: RequestHandler = async ({ cookies }) => { try { - const sessionToken = cookies.get("session"); + const accessToken = cookies.get("access_token"); - if (!sessionToken) { - return json({ error: "No active session found" }, { status: 400 }); + if (!accessToken) { + return json({ error: "No access token found" }, { status: 400 }); } - await SessionService.logout(sessionToken); - - cookies.delete("session", { + // Simply clear the access token cookie - no need to invalidate sessions + cookies.delete("access_token", { path: "/", httpOnly: true, secure: true, diff --git a/src/routes/api/auth/refresh/+server.ts b/src/routes/api/auth/refresh/+server.ts index 0f2ce35..ddfcd53 100644 --- a/src/routes/api/auth/refresh/+server.ts +++ b/src/routes/api/auth/refresh/+server.ts @@ -8,25 +8,8 @@ const logger = new UniversalLogger().setContext("AuthRefreshAPI"); registerOpenAPIRoute("/auth/refresh", "POST", { summary: "Refresh access token", - description: "Generate new access and refresh tokens using existing refresh token", + description: "Generate new access and refresh tokens using the current session", tags: ["Authentication"], - requestBody: { - description: "Refresh token data", - content: { - "application/json": { - schema: { - type: "object", - properties: { - refreshToken: { - type: "string", - description: "Current refresh token" - } - }, - required: ["refreshToken"] - } - } - } - }, responses: { "200": { description: "Token refresh successful", @@ -36,17 +19,15 @@ registerOpenAPIRoute("/auth/refresh", "POST", { type: "object", properties: { message: { type: "string", description: "Success message" }, - accessToken: { type: "string", description: "New JWT access token" }, - refreshToken: { type: "string", description: "New refresh token" }, expiresAt: { type: "string", format: "date-time", description: "New expiration time" } }, - required: ["message", "accessToken", "refreshToken", "expiresAt"] + required: ["message", "expiresAt"] } } } }, "400": { - description: "Invalid refresh token", + description: "No valid session found", content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } @@ -72,16 +53,26 @@ registerOpenAPIRoute("/auth/refresh", "POST", { } }); -export const POST: RequestHandler = async ({ request }) => { +export const POST: RequestHandler = async ({ locals, cookies }) => { try { - const body = await request.json(); - const { refreshToken } = body; + // Get current access token from cookie to extract session info + const accessToken = cookies.get("access_token"); - if (!refreshToken) { - return json({ error: "Refresh token is required" }, { status: 400 }); + if (!accessToken) { + return json({ error: "Access token is required" }, { status: 400 }); } - const result = await SessionService.refreshSession(refreshToken); + // The user object is already available from authHandle, + // so we can create a new session based on that + if (!locals.user) { + return json({ error: "Invalid user context" }, { status: 400 }); + } + + const result = await SessionService.createSession( + locals.user.userId as string, + "", // IP address - could be extracted from request if needed + undefined // user agent + ); if (!result) { return json({ error: "Invalid or expired refresh token" }, { status: 401 }); @@ -89,10 +80,17 @@ export const POST: RequestHandler = async ({ request }) => { logger.info("Token refresh successful"); + // Set HTTP-only cookie for new access token + cookies.set("access_token", result.accessToken, { + httpOnly: true, + secure: true, + sameSite: "strict", + path: "/", + maxAge: 60 * 60 * 24 * 7 // 7 days + }); + return json({ message: "Token refresh successful", - accessToken: result.accessToken, - refreshToken: result.refreshToken, expiresAt: result.expiresAt.toISOString() }); } catch (error) { diff --git a/src/routes/api/auth/session/+server.ts b/src/routes/api/auth/session/+server.ts index 4e395e0..720ddbe 100644 --- a/src/routes/api/auth/session/+server.ts +++ b/src/routes/api/auth/session/+server.ts @@ -1,5 +1,4 @@ import { json } from "@sveltejs/kit"; -import { SessionService } from "$lib/server/auth/session-service"; import type { RequestHandler } from "./$types"; import { registerOpenAPIRoute } from "$lib/server/openapi"; import { UniversalLogger } from "$lib/logger"; @@ -67,41 +66,28 @@ registerOpenAPIRoute("/auth/session", "GET", { } }); -export const GET: RequestHandler = async ({ cookies }) => { +export const GET: RequestHandler = async ({ locals }) => { try { - const sessionToken = cookies.get("session"); - - if (!sessionToken) { - return json({ authenticated: false, message: "No session found" }, { status: 401 }); + // User is already authenticated via authHandle + if (!locals.user) { + return json({ authenticated: false, message: "Not authenticated" }, { status: 401 }); } - const sessionData = await SessionService.validateSession(sessionToken); - - if (!sessionData) { - cookies.delete("session", { - path: "/", - httpOnly: true, - secure: true, - sameSite: "strict" - }); - return json({ authenticated: false, message: "Invalid or expired session" }, { status: 401 }); - } - - logger.debug("Session validated", { userId: sessionData.user.id }); + logger.debug("Session check", { userId: locals.user.userId }); return json({ authenticated: true, user: { - id: sessionData.user.id, - email: sessionData.user.email, - name: sessionData.user.name, - role: sessionData.user.role, - tenantId: sessionData.user.tenantId + id: locals.user.userId, + email: locals.user.email, + name: locals.user.name, + role: locals.user.role, + tenantId: locals.user.tenantId }, - expiresAt: sessionData.expiresAt.toISOString() + expiresAt: new Date(locals.user.exp! * 1000).toISOString() }); } catch (error) { - logger.error("Session validation error:", { error: String(error) }); + logger.error("Session check error:", { error: String(error) }); return json({ error: "Internal server error" }, { status: 500 }); } }; diff --git a/src/routes/api/auth/sessions/+server.ts b/src/routes/api/auth/sessions/+server.ts index d46c394..bcb5861 100644 --- a/src/routes/api/auth/sessions/+server.ts +++ b/src/routes/api/auth/sessions/+server.ts @@ -1,5 +1,4 @@ import { json } from "@sveltejs/kit"; -import { SessionService } from "$lib/server/auth/session-service"; import type { RequestHandler } from "./$types"; import { registerOpenAPIRoute } from "$lib/server/openapi"; import { UniversalLogger } from "$lib/logger"; @@ -109,72 +108,54 @@ registerOpenAPIRoute("/auth/sessions", "DELETE", { } }); -export const GET: RequestHandler = async ({ cookies }) => { +export const GET: RequestHandler = async ({ locals }) => { try { - const sessionToken = cookies.get("session"); - - if (!sessionToken) { + // With access token-only approach, we don't track active sessions + // Return current token info only + if (!locals.user) { return json({ error: "Not authenticated" }, { status: 401 }); } - const sessionData = await SessionService.validateSession(sessionToken); + const currentSession = { + id: locals.user.sessionId || "current", + ipAddress: "unknown", // Would need to be tracked separately + userAgent: "unknown", // Would need to be tracked separately + createdAt: new Date(locals.user.iat! * 1000).toISOString(), + lastUsedAt: new Date().toISOString(), + expiresAt: new Date(locals.user.exp! * 1000).toISOString(), + current: true + }; - if (!sessionData) { - return json({ error: "Invalid session" }, { status: 401 }); - } - - const sessions = await SessionService.getActiveSessions(sessionData.user.id); - - const formattedSessions = sessions.map((session) => ({ - id: session.id, - ipAddress: session.ipAddress, - userAgent: session.userAgent, - createdAt: session.createdAt?.toISOString(), - lastUsedAt: session.lastUsedAt?.toISOString(), - expiresAt: session.expiresAt.toISOString(), - current: session.sessionToken === sessionToken - })); - - logger.debug("Active sessions retrieved", { - userId: sessionData.user.id, - sessionCount: sessions.length + logger.debug("Current session info retrieved", { + userId: locals.user.userId }); - return json({ sessions: formattedSessions }); + return json({ sessions: [currentSession] }); } catch (error) { logger.error("Get sessions error:", { error: String(error) }); return json({ error: "Internal server error" }, { status: 500 }); } }; -export const DELETE: RequestHandler = async ({ cookies }) => { +export const DELETE: RequestHandler = async ({ locals, cookies }) => { try { - const sessionToken = cookies.get("session"); - - if (!sessionToken) { + // With access token-only approach, just clear the current token + if (!locals.user) { return json({ error: "Not authenticated" }, { status: 401 }); } - const sessionData = await SessionService.validateSession(sessionToken); - - if (!sessionData) { - return json({ error: "Invalid session" }, { status: 401 }); - } - - await SessionService.logoutAllSessions(sessionData.user.id); - - cookies.delete("session", { + cookies.delete("access_token", { path: "/", httpOnly: true, secure: true, sameSite: "strict" }); - logger.info("All sessions logged out", { userId: sessionData.user.id }); + logger.info("Access token cleared", { userId: locals.user.userId }); - return json({ message: "All sessions logged out successfully" }); + return json({ message: "Logged out successfully" }); } catch (error) { - logger.error("Logout all sessions error:", { error: String(error) }); + logger.error("Logout error:", { error: String(error) }); return json({ error: "Internal server error" }, { status: 500 }); } }; diff --git a/src/routes/api/auth/sessions/[sessionId]/+server.ts b/src/routes/api/auth/sessions/[sessionId]/+server.ts index efc2e8e..5edcae1 100644 --- a/src/routes/api/auth/sessions/[sessionId]/+server.ts +++ b/src/routes/api/auth/sessions/[sessionId]/+server.ts @@ -1,5 +1,4 @@ import { json } from "@sveltejs/kit"; -import { SessionService } from "$lib/server/auth/session-service"; import type { RequestHandler } from "./$types"; import { registerOpenAPIRoute } from "$lib/server/openapi"; import { UniversalLogger } from "$lib/logger"; @@ -69,46 +68,32 @@ registerOpenAPIRoute("/auth/sessions/{sessionId}", "DELETE", { } }); -export const DELETE: RequestHandler = async ({ params, cookies }) => { +export const DELETE: RequestHandler = async ({ params, locals, cookies }) => { try { - const sessionToken = cookies.get("session"); const sessionIdToRevoke = params.sessionId; - if (!sessionToken) { + if (!locals.user) { return json({ error: "Not authenticated" }, { status: 401 }); } - const sessionData = await SessionService.validateSession(sessionToken); - - if (!sessionData) { - return json({ error: "Invalid session" }, { status: 401 }); + // With access token-only approach, we can only revoke the current token + const currentSessionId = locals.user.sessionId || "current"; + + if (sessionIdToRevoke !== currentSessionId) { + return json({ error: "Can only revoke current session" }, { status: 404 }); } - // Get all user sessions to verify ownership - const userSessions = await SessionService.getActiveSessions(sessionData.user.id); - const targetSession = userSessions.find((session) => session.id === sessionIdToRevoke); + // Clear access token cookie + cookies.delete("access_token", { + path: "/", + httpOnly: true, + secure: true, + sameSite: "strict" + }); - if (!targetSession) { - return json({ error: "Session not found" }, { status: 404 }); - } - - // Check if trying to revoke current session - if (targetSession.sessionToken === sessionToken) { - // Clear current session cookie - cookies.delete("session", { - path: "/", - httpOnly: true, - secure: true, - sameSite: "strict" - }); - } - - await SessionService.revokeSession(sessionIdToRevoke); - - logger.info("Session revoked", { - userId: sessionData.user.id, - revokedSessionId: sessionIdToRevoke, - currentSession: targetSession.sessionToken === sessionToken + logger.info("Access token revoked", { + userId: locals.user.userId, + revokedSessionId: sessionIdToRevoke }); return json({ message: "Session revoked successfully" }); diff --git a/src/server-hooks/authHandle.ts b/src/server-hooks/authHandle.ts index 720530e..4d4704f 100644 --- a/src/server-hooks/authHandle.ts +++ b/src/server-hooks/authHandle.ts @@ -1,13 +1,11 @@ import type { Handle } from "@sveltejs/kit"; -import { SessionService } from "$lib/server/auth/session-service"; import { verifyAccessToken } from "$lib/server/auth/jwt-utils"; import { UniversalLogger } from "$lib/logger"; -import type { JWTPayload } from "jose"; import { AuthorizationService } from "$lib/server/auth/authorization-service"; const logger = new UniversalLogger().setContext("AuthHandle"); -const SESSION_COOKIE_NAME = "session"; +const ACCESS_TOKEN_COOKIE_NAME = "access_token"; const PROTECTED_PATHS = ["/api/admin", "/api/tenant-admin", "/api/tenants", "/api/auth/register"]; const PUBLIC_PATHS = [ "/api/auth/challenge", @@ -60,20 +58,23 @@ export const authHandle: Handle = async ({ event, resolve }) => { ); } - let sessionToken: string | null = null; let accessToken: string | null = null; - const sessionCookie = event.cookies.get(SESSION_COOKIE_NAME); - if (sessionCookie) { - sessionToken = sessionCookie; + // Get access token from cookie + const accessTokenCookie = event.cookies.get(ACCESS_TOKEN_COOKIE_NAME); + if (accessTokenCookie) { + accessToken = accessTokenCookie; } - const authHeader = request.headers.get("authorization"); - if (authHeader?.startsWith("Bearer ")) { - accessToken = authHeader.substring(7); + // Fallback: check Authorization header + if (!accessToken) { + const authHeader = request.headers.get("authorization"); + if (authHeader?.startsWith("Bearer ")) { + accessToken = authHeader.substring(7); + } } - if (!sessionToken && !accessToken) { + if (!accessToken) { logger.warn(`Authentication required for ${path}`); return new Response(JSON.stringify({ error: "Authentication required" }), { status: 401, @@ -81,39 +82,17 @@ export const authHandle: Handle = async ({ event, resolve }) => { }); } - let user: JWTPayload | null = null; - - if (accessToken) { - user = await verifyAccessToken(accessToken); - if (!user) { - logger.warn(`Invalid access token for ${path}`); - return new Response(JSON.stringify({ error: "Invalid access token" }), { - status: 401, - headers: { "Content-Type": "application/json" } - }); - } - } else if (sessionToken) { - const sessionData = await SessionService.validateSession(sessionToken); - if (!sessionData) { - logger.warn(`Invalid session for ${path}`); - return new Response(JSON.stringify({ error: "Invalid session" }), { - status: 401, - headers: { "Content-Type": "application/json" } - }); - } - user = await verifyAccessToken(sessionData.accessToken); - } - + // Verify access token + const user = await verifyAccessToken(accessToken); if (!user) { - logger.warn(`Authentication failed for ${path}`); - return new Response(JSON.stringify({ error: "Authentication failed" }), { + logger.warn(`Invalid access token for ${path}`); + return new Response(JSON.stringify({ error: "Invalid access token" }), { status: 401, headers: { "Content-Type": "application/json" } }); } event.locals.user = user; - event.locals.sessionToken = sessionToken || undefined; if (isGlobalAdminPath && !AuthorizationService.hasRole(user, "GLOBAL_ADMIN")) { return new Response(JSON.stringify({ error: "Authentication failed" }), {