diff --git a/apps/partner-sas/components/BookingFlowProviders.tsx b/apps/partner-sas/components/BookingFlowProviders.tsx index c438b3a07..e57e3b579 100644 --- a/apps/partner-sas/components/BookingFlowProviders.tsx +++ b/apps/partner-sas/components/BookingFlowProviders.tsx @@ -3,15 +3,11 @@ import { useSession } from "next-auth/react" import { BookingFlowContextProvider } from "@scandic-hotels/booking-flow/BookingFlowContextProvider" -import { dt } from "@scandic-hotels/common/dt" -import { createLogger } from "@scandic-hotels/common/logger/createLogger" import { trpc } from "@scandic-hotels/trpc/client" +import { isValidSession } from "@scandic-hotels/trpc/utils/session" -import type { Session } from "next-auth" import type { ComponentProps, ReactNode } from "react" -const logger = createLogger("BookingFlowProviders") - export function BookingFlowProviders({ children }: { children: ReactNode }) { const user = useBookingFlowUser() const isLinkedUser = @@ -36,7 +32,7 @@ type BookingFlowUser = BookingFlowContextData["user"] function useBookingFlowUser(): BookingFlowUser { const { data: session } = useSession() - const hasValidSession = isValidClientSession(session) + const hasValidSession = isValidSession(session) const { data: euroBonusProfile, @@ -66,25 +62,3 @@ function useBookingFlowUser(): BookingFlowUser { }, } } - -function isValidClientSession(session: Session | null) { - if (!session) { - return false - } - if (session.error) { - logger.error(`Session error: ${session.error}`) - return false - } - - if (session.token.error) { - logger.error(`Session token error: ${session.token.error}`) - return false - } - const expiresAt = dt(session.token.expires_at) - if (session.token.expires_at && expiresAt.isBefore(dt())) { - logger.warn(`Session expired: ${expiresAt.toISOString()}`) - return false - } - - return true -} diff --git a/apps/partner-sas/hooks/useSocialSession.ts b/apps/partner-sas/hooks/useSocialSession.ts index d906c997d..c4bc7ba3d 100644 --- a/apps/partner-sas/hooks/useSocialSession.ts +++ b/apps/partner-sas/hooks/useSocialSession.ts @@ -6,6 +6,7 @@ import { useEffect } from "react" import { dt } from "@scandic-hotels/common/dt" import { createLogger } from "@scandic-hotels/common/logger/createLogger" +import { isValidSession } from "@scandic-hotels/trpc/utils/session" import type { User } from "next-auth" @@ -26,10 +27,12 @@ export function useSocialSession() { function useSocialSessionQuery() { const { data: session } = useSession() + const enabled = isValidSession(session) && isUserLinked(session?.user) + return useQuery({ queryKey: ["socialSession"], queryFn: getSocialSession, - enabled: !!session, + enabled: enabled, refetchInterval: getTime(1, "m"), }) } @@ -38,7 +41,8 @@ function useAutoLogin() { const { data: session } = useSession() const { isSuccess, data: socialSession } = useSocialSessionQuery() - const isLinked = isLinkedUser(session?.user) ? session.user.isLinked : false + const isLinked = isUserLinked(session?.user) + useEffect(() => { if (!isLinked) { autoLoginLogger.info("User is not linked") @@ -174,11 +178,10 @@ function getTime(value: number, unit: "m" | "s") { } } -function isLinkedUser( - user: User | undefined -): user is User & { isLinked: boolean } { +function isUserLinked(user: User | undefined): boolean { if (user && "isLinked" in user) { - return true + return !!user.isLinked } + return false } diff --git a/apps/scandic-web/components/Header/MainMenu/MyPagesMenuWrapper/index.tsx b/apps/scandic-web/components/Header/MainMenu/MyPagesMenuWrapper/index.tsx index 2427a790e..6f2335c53 100644 --- a/apps/scandic-web/components/Header/MainMenu/MyPagesMenuWrapper/index.tsx +++ b/apps/scandic-web/components/Header/MainMenu/MyPagesMenuWrapper/index.tsx @@ -9,9 +9,9 @@ import { Avatar } from "@scandic-hotels/design-system/Avatar" import { LoginButton } from "@scandic-hotels/design-system/LoginButton" import { Typography } from "@scandic-hotels/design-system/Typography" import { trpc } from "@scandic-hotels/trpc/client" +import { isValidSession } from "@scandic-hotels/trpc/utils/session" import useLang from "@/hooks/useLang" -import { isValidClientSession } from "@/utils/clientSession" import { trackLoginClick } from "@/utils/tracking" import MyPagesMenu, { MyPagesMenuSkeleton } from "../MyPagesMenu" @@ -27,7 +27,7 @@ export default function MyPagesMenuWrapper() { const loginPathname = useLazyPathname({ includeSearchParams: true }) const { data: session } = useSession() - const isUserLoggedIn = isValidClientSession(session) + const isUserLoggedIn = isValidSession(session) const { data: user, isLoading: isLoadingUser } = trpc.user.name.useQuery() const { data: membership, isLoading: isLoadingMembership } = diff --git a/apps/scandic-web/components/UserExists.tsx b/apps/scandic-web/components/UserExists.tsx index ba42ff785..91c8b1582 100644 --- a/apps/scandic-web/components/UserExists.tsx +++ b/apps/scandic-web/components/UserExists.tsx @@ -5,15 +5,15 @@ import { useSession } from "next-auth/react" import { logoutSafely } from "@scandic-hotels/common/constants/routes/handleAuth" import { trpc } from "@scandic-hotels/trpc/client" +import { isValidSession } from "@scandic-hotels/trpc/utils/session" import { userNotFound } from "@/constants/routes/errorPages" import useLang from "@/hooks/useLang" -import { isValidClientSession } from "@/utils/clientSession" export function UserExists() { const { data: session } = useSession() - const isUserLoggedIn = isValidClientSession(session) + const isUserLoggedIn = isValidSession(session) const lang = useLang() const { isLoading: isLoadingUser, error } = trpc.user.get.useQuery( diff --git a/apps/scandic-web/hooks/useIsUserLoggedIn.ts b/apps/scandic-web/hooks/useIsUserLoggedIn.ts index fc36a6b63..be2ee881a 100644 --- a/apps/scandic-web/hooks/useIsUserLoggedIn.ts +++ b/apps/scandic-web/hooks/useIsUserLoggedIn.ts @@ -1,9 +1,9 @@ import { useSession } from "next-auth/react" -import { isValidClientSession } from "@/utils/clientSession" +import { isValidSession } from "@scandic-hotels/trpc/utils/session" export function useIsUserLoggedIn() { const { data: session } = useSession() - const isUserLoggedIn = isValidClientSession(session) + const isUserLoggedIn = isValidSession(session) return isUserLoggedIn } diff --git a/apps/scandic-web/utils/clientSession.ts b/apps/scandic-web/utils/clientSession.ts deleted file mode 100644 index a2c220777..000000000 --- a/apps/scandic-web/utils/clientSession.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { createLogger } from "@scandic-hotels/common/logger/createLogger" - -import type { Session } from "next-auth" - -const logger = createLogger("clientSession") -export function isValidClientSession(session: Session | null) { - if (!session) { - return false - } - if (session.error) { - logger.error(`Session error: ${session.error}`) - return false - } - - if (session.token.error) { - logger.error(`Session token error: ${session.token.error}`) - return false - } - if (session.token.expires_at && session.token.expires_at < Date.now()) { - logger.error(`Session expired: ${session.token.expires_at}`) - return false - } - - return true -} diff --git a/packages/trpc/lib/utils/session.test.ts b/packages/trpc/lib/utils/session.test.ts new file mode 100644 index 000000000..b3ea73b2d --- /dev/null +++ b/packages/trpc/lib/utils/session.test.ts @@ -0,0 +1,58 @@ +import { beforeEach, describe, expect, it, vi } from "vitest" + +import { isValidSession } from "./session" + +vi.mock("@scandic-hotels/common/logger/createLogger", () => { + return { + createLogger: (_name: string) => ({ + error: vi.fn(), + debug: vi.fn(), + }), + } +}) + +describe("isValidSession", () => { + beforeEach(() => { + vi.restoreAllMocks() + }) + + it("returns false for null session and does not log", () => { + expect(isValidSession(null)).toBe(false) + }) + + it("returns false and logs when session.error is present", () => { + const s: any = { error: "bad" } + expect(isValidSession(s)).toBe(false) + }) + + it("returns false and logs when session.token.error is present", () => { + const s: any = { token: { error: "tokfail" } } + expect(isValidSession(s)).toBe(false) + }) + + it("returns false and logs when token.expires_at is in the past", () => { + const now = 1_700_000_000_000 + vi.spyOn(Date, "now").mockReturnValue(now) + const s: any = { token: { expires_at: now - 1000 } } + expect(isValidSession(s)).toBe(false) + }) + + it("returns true when token.expires_at equals Date.now()", () => { + const now = 1_700_000_000_000 + vi.spyOn(Date, "now").mockReturnValue(now) + const s: any = { token: { expires_at: now } } + expect(isValidSession(s)).toBe(true) + }) + + it("returns true for session without token.expires_at", () => { + const s: any = { token: { access: "ok" } } + expect(isValidSession(s)).toBe(true) + }) + + it("returns true for a fully valid session with future expires_at", () => { + const now = 1_700_000_000_000 + vi.spyOn(Date, "now").mockReturnValue(now) + const s: any = { token: { expires_at: now + 10_000 } } + expect(isValidSession(s)).toBe(true) + }) +}) diff --git a/packages/trpc/lib/utils/session.ts b/packages/trpc/lib/utils/session.ts index 0b283e585..847629d51 100644 --- a/packages/trpc/lib/utils/session.ts +++ b/packages/trpc/lib/utils/session.ts @@ -1,11 +1,9 @@ -import "server-only" - import { createLogger } from "@scandic-hotels/common/logger/createLogger" import type { Session } from "next-auth" +const sessionLogger = createLogger("session") export function isValidSession(session: Session | null): session is Session { - const sessionLogger = createLogger("session") if (!session) { return false } @@ -15,13 +13,12 @@ export function isValidSession(session: Session | null): session is Session { return false } - const token = session.token - - if (token?.error) { - sessionLogger.error(`Session token error: ${token.error}`) + if (session.token?.error) { + sessionLogger.error(`Session token error: ${session.token.error}`) return false } - if (token?.expires_at && token.expires_at < Date.now()) { + + if (session.token?.expires_at && session.token.expires_at < Date.now()) { sessionLogger.debug(`Session expired: ${session.token.expires_at}`) return false }