diff --git a/package-lock.json b/package-lock.json index e6e60a6d1b..8287c7e6c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1745,11 +1745,11 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.6.tgz", - "integrity": "sha512-lWfvAIFNWMlCsU0DRUun2GpFwZdGTukLaHJqRh1JRb80NdAP5Sb1HDHB5X9P9OtgZHQl089UzQkpYlBq2VTPRw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz", + "integrity": "sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.6" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -11927,9 +11927,9 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", - "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", "dependencies": { "@babel/types": "^7.20.7" } diff --git a/packages/web-main/src/components/Navbar/UserDropdown.tsx b/packages/web-main/src/components/Navbar/UserDropdown.tsx index 58694463d9..5e7f76db40 100644 --- a/packages/web-main/src/components/Navbar/UserDropdown.tsx +++ b/packages/web-main/src/components/Navbar/UserDropdown.tsx @@ -56,18 +56,15 @@ const Name = styled.div` `; export const UserDropdown = () => { - const { session } = useAuth(); const { logOut } = useAuth(); const navigate = useNavigate(); - const { data, isPending } = useQuery(userMeQueryOptions()); const hasMultipleDomains = isPending === false && data && data.domains && data.domains.length > 1 ? true : false; - // TODO: this should be a fallback component, to stil try to logout. - if (session === null) return
could not get session
; + if (!data) return
could not get user information
; - const { name, email } = session; + const { name, email } = data.user; return ( diff --git a/packages/web-main/src/components/Navbar/index.tsx b/packages/web-main/src/components/Navbar/index.tsx index c3fd1c6497..fc57706f02 100644 --- a/packages/web-main/src/components/Navbar/index.tsx +++ b/packages/web-main/src/components/Navbar/index.tsx @@ -53,7 +53,7 @@ const domainLinks: NavbarLink[] = [ { label: 'Players', linkProps: { - to: '/players' as any, + to: '/players', }, icon: , requiredPermissions: [PERMISSIONS.ReadPlayers], @@ -61,7 +61,7 @@ const domainLinks: NavbarLink[] = [ { label: 'Users', linkProps: { - to: '/users' as any, + to: '/users', }, icon: , requiredPermissions: [PERMISSIONS.ReadUsers], @@ -112,8 +112,6 @@ export interface NavbarLink { export const renderLink = ({ linkProps, icon, label, requiredPermissions }: NavbarLink) => (
- {/* eslint-disable-next-line @typescript-eslint/ban-ts-comment*/} - {/* @ts-ignore reusable link */} {cloneElement(icon, { size: 20, key: `icon-${linkProps.to}` })} diff --git a/packages/web-main/src/components/PermissionsGuard.tsx b/packages/web-main/src/components/PermissionsGuard.tsx index 1e60c596cc..900a5856fe 100644 --- a/packages/web-main/src/components/PermissionsGuard.tsx +++ b/packages/web-main/src/components/PermissionsGuard.tsx @@ -1,7 +1,7 @@ import { PERMISSIONS } from '@takaro/apiclient'; import { PermissionsGuard as Guard } from '@takaro/lib-components'; import { RequiredPermissions } from '@takaro/lib-components'; -import { useAuth } from 'hooks/useAuth'; +import { useSession } from 'hooks/useSession'; import { FC, PropsWithChildren, ReactElement, useMemo } from 'react'; interface PermissionsGuardProps { @@ -14,7 +14,7 @@ export const PermissionsGuard: FC> = ({ children, fallback, }) => { - const { session } = useAuth(); + const { session } = useSession(); const userPermissions = useMemo(() => { if (!session) { diff --git a/packages/web-main/src/hooks/useAuth.tsx b/packages/web-main/src/hooks/useAuth.tsx index e27813c1b4..3efdad53a3 100644 --- a/packages/web-main/src/hooks/useAuth.tsx +++ b/packages/web-main/src/hooks/useAuth.tsx @@ -1,11 +1,10 @@ import { useQueryClient } from '@tanstack/react-query'; import { UserOutputWithRolesDTO } from '@takaro/apiclient'; -import { createContext, useCallback, useContext, useEffect, useState } from 'react'; +import { createContext, useCallback, useContext } from 'react'; import { useOry } from './useOry'; import * as Sentry from '@sentry/react'; import { DateTime } from 'luxon'; import { getApiClient } from 'util/getApiClient'; -import { redirect } from '@tanstack/react-router'; const SESSION_EXPIRES_AFTER_MINUTES = 5; @@ -16,8 +15,7 @@ interface ExpirableSession { export interface IAuthContext { logOut: () => Promise; - isAuthenticated: boolean; - session: UserOutputWithRolesDTO; + getSession: () => Promise; login: (user: UserOutputWithRolesDTO) => void; } @@ -35,19 +33,27 @@ function getLocalSession() { return expirableSession.session; } +function setLocalSession(session: UserOutputWithRolesDTO | null) { + if (session) { + const expirableSession: ExpirableSession = { + session, + expiresAt: DateTime.now().plus({ minutes: SESSION_EXPIRES_AFTER_MINUTES }).toISO(), + }; + localStorage.setItem('session', JSON.stringify(expirableSession)); + } else { + localStorage.removeItem('session'); + } +} + export const AuthContext = createContext(null); export function AuthProvider({ children }: { children: React.ReactNode }) { const queryClient = useQueryClient(); const { oryClient } = useOry(); - const [user, setUser] = useState(getLocalSession()); - - const isAuthenticated = !!user; const logOut = useCallback(async () => { const logoutFlowRes = await oryClient.createBrowserLogoutFlow(); - setStoredSession(null); - setUser(null); + setLocalSession(null); queryClient.clear(); window.location.href = logoutFlowRes.data.logout_url; // Extra clean up is done in /logout-return @@ -55,29 +61,11 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { }, [oryClient, queryClient]); const login = useCallback((session: UserOutputWithRolesDTO) => { - setStoredSession(session); + setLocalSession(session); Sentry.setUser({ id: session.id, email: session.email, username: session.name }); - setUser(session); }, []); - const refreshSession = useCallback(async () => { - try { - const response = await getApiClient().user.userControllerMe({ - headers: { - 'Cache-Control': 'no-cache', - }, - }); - const newSession = response.data.data.user; - login(newSession); - } catch (error) { - setStoredSession(null); - setUser(null); - queryClient.clear(); - redirect({ to: '/login' }); - } - }, [login, queryClient]); - - const getSession = useCallback(async (): Promise => { + const getSession = async function (): Promise { const session = getLocalSession(); if (session) { @@ -85,45 +73,31 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { } try { - await refreshSession(); - return JSON.parse(localStorage.getItem('session')!).session; + const newSession = ( + await getApiClient().user.userControllerMe({ + headers: { + 'Cache-Control': 'no-cache', + }, + }) + ).data.data.user; + setLocalSession(session); + return newSession; } catch (error) { + // logout if session is invalid localStorage.removeItem('session'); - return null; - } - }, [refreshSession]); - - useEffect(() => { - async function fetchSession() { - const storedSession = await getSession(); - if (!storedSession) { - refreshSession(); - } else { - setUser(storedSession); - } - } - fetchSession(); - }, [getSession, refreshSession]); - - function setStoredSession(session: UserOutputWithRolesDTO | null) { - if (session) { - const expirableSession: ExpirableSession = { - session, - expiresAt: DateTime.now().plus({ minutes: SESSION_EXPIRES_AFTER_MINUTES }).toISO(), - }; - localStorage.setItem('session', JSON.stringify(expirableSession)); - } else { - localStorage.removeItem('session'); + setLocalSession(null); + queryClient.clear(); + window.location.href = '/login'; + throw 'should not have no session and not be redirected to login'; } - } + }; return ( {children} diff --git a/packages/web-main/src/hooks/useHasPermission.tsx b/packages/web-main/src/hooks/useHasPermission.tsx index 47e93e9816..b97f5de709 100644 --- a/packages/web-main/src/hooks/useHasPermission.tsx +++ b/packages/web-main/src/hooks/useHasPermission.tsx @@ -2,10 +2,10 @@ import { useMemo } from 'react'; import { RequiredPermissions } from '@takaro/lib-components'; import { hasPermissionHelper } from '@takaro/lib-components/src/components/other/PermissionsGuard'; import { PERMISSIONS, UserOutputWithRolesDTO } from '@takaro/apiclient'; -import { useAuth } from 'hooks/useAuth'; +import { useSession } from './useSession'; export const useHasPermission = (requiredPermissions: RequiredPermissions) => { - const { session } = useAuth(); + const { session } = useSession(); const userPermissions = useMemo(() => { if (!session) { diff --git a/packages/web-main/src/hooks/useSession.tsx b/packages/web-main/src/hooks/useSession.tsx new file mode 100644 index 0000000000..7eacd34eec --- /dev/null +++ b/packages/web-main/src/hooks/useSession.tsx @@ -0,0 +1,16 @@ +import { UserOutputWithRolesDTO } from '@takaro/apiclient'; +import { createContext, useContext } from 'react'; + +export interface ISessionContext { + session: UserOutputWithRolesDTO; +} + +export const SessionContext = createContext({} as any); + +export function useSession() { + const context = useContext(SessionContext); + if (!context) { + throw new Error('useAuth must be used within an AuthProvider'); + } + return context; +} diff --git a/packages/web-main/src/queryClient.ts b/packages/web-main/src/queryClient.ts index 5e9cbb65aa..c67e50aff6 100644 --- a/packages/web-main/src/queryClient.ts +++ b/packages/web-main/src/queryClient.ts @@ -14,7 +14,6 @@ const options = { Sentry.setTag('traceId', traceId); } } - Sentry.captureException(error); return true; } diff --git a/packages/web-main/src/router.tsx b/packages/web-main/src/router.tsx index c96b9e8569..ee605118ba 100644 --- a/packages/web-main/src/router.tsx +++ b/packages/web-main/src/router.tsx @@ -2,8 +2,9 @@ import { queryClient } from './queryClient'; import { routeTree } from './routeTree.gen'; import { createRouter } from '@tanstack/react-router'; import { QueryClient } from '@tanstack/react-query'; -import { IAuthContext } from 'hooks/useAuth'; import { DefaultErrorComponent } from 'components/ErrorComponent'; +import { IAuthContext } from 'hooks/useAuth'; +import { UserOutputWithRolesDTO } from '@takaro/apiclient'; export interface RouterContext { queryClient: QueryClient; @@ -14,10 +15,9 @@ export const router = createRouter({ routeTree, context: { auth: { - isAuthenticated: false, logOut: async () => {}, - session: undefined!, login: () => {}, + getSession: async () => ({} as Promise), }, queryClient: queryClient, }, diff --git a/packages/web-main/src/routes/_auth.tsx b/packages/web-main/src/routes/_auth.tsx index be23a6f0ab..7f93c2f879 100644 --- a/packages/web-main/src/routes/_auth.tsx +++ b/packages/web-main/src/routes/_auth.tsx @@ -1,11 +1,27 @@ import { Outlet, createFileRoute, redirect } from '@tanstack/react-router'; +import { SessionContext } from 'hooks/useSession'; export const Route = createFileRoute('/_auth')({ - beforeLoad: ({ context }) => { - if (context.auth.isAuthenticated === false) { + beforeLoad: async ({ context }) => { + const session = await context.auth.getSession(); + if (!session) { const redirectPath = location.pathname === '/login' ? '/' : location.pathname; throw redirect({ to: '/login', search: { redirect: redirectPath } }); } }, - component: () => , + + loader: async ({ context }) => { + return await context.auth.getSession(); + }, + component: Component, }); + +function Component() { + const session = Route.useLoaderData(); + + return ( + + + + ); +} diff --git a/packages/web-main/src/routes/_auth/_global/dashboard.tsx b/packages/web-main/src/routes/_auth/_global/dashboard.tsx index 28919ebd1e..6d54a05bf7 100644 --- a/packages/web-main/src/routes/_auth/_global/dashboard.tsx +++ b/packages/web-main/src/routes/_auth/_global/dashboard.tsx @@ -13,7 +13,8 @@ import { PlayersOnlineStatsQueryOptions, ActivityStatsQueryOptions } from 'queri export const Route = createFileRoute('/_auth/_global/dashboard')({ beforeLoad: async ({ context }) => { - if (!hasPermission(context.auth.session, ['READ_EVENTS'])) { + const session = await context.auth.getSession(); + if (!hasPermission(session, ['READ_EVENTS'])) { throw redirect({ to: '/forbidden' }); } }, diff --git a/packages/web-main/src/routes/_auth/_global/events.tsx b/packages/web-main/src/routes/_auth/_global/events.tsx index 170ad6ae27..7dc852fdbf 100644 --- a/packages/web-main/src/routes/_auth/_global/events.tsx +++ b/packages/web-main/src/routes/_auth/_global/events.tsx @@ -143,7 +143,8 @@ type EventSearch = z.infer; export const Route = createFileRoute('/_auth/_global/events')({ validateSearch: eventSearchSchema, beforeLoad: async ({ context }) => { - if (!hasPermission(context.auth.session, ['READ_EVENTS', 'READ_GAMESERVERS', 'READ_PLAYERS', 'READ_USERS'])) { + const session = await context.auth.getSession(); + if (!hasPermission(session, ['READ_EVENTS', 'READ_GAMESERVERS', 'READ_PLAYERS', 'READ_USERS'])) { throw redirect({ to: '/forbidden' }); } }, diff --git a/packages/web-main/src/routes/_auth/_global/gameservers.create.import.tsx b/packages/web-main/src/routes/_auth/_global/gameservers.create.import.tsx index fb4c983e39..b29fc72053 100644 --- a/packages/web-main/src/routes/_auth/_global/gameservers.create.import.tsx +++ b/packages/web-main/src/routes/_auth/_global/gameservers.create.import.tsx @@ -14,8 +14,9 @@ export interface IFormInputs { } export const Route = createFileRoute('/_auth/_global/gameservers/create/import')({ - beforeLoad: ({ context }) => { - if (!hasPermission(context.auth.session, ['MANAGE_GAMESERVERS'])) { + beforeLoad: async ({ context }) => { + const session = await context.auth.getSession(); + if (!hasPermission(session, ['MANAGE_GAMESERVERS'])) { throw redirect({ to: '/forbidden' }); } }, diff --git a/packages/web-main/src/routes/_auth/_global/gameservers.create.index.tsx b/packages/web-main/src/routes/_auth/_global/gameservers.create.index.tsx index 72ef0cd793..f9adcd2520 100644 --- a/packages/web-main/src/routes/_auth/_global/gameservers.create.index.tsx +++ b/packages/web-main/src/routes/_auth/_global/gameservers.create.index.tsx @@ -8,8 +8,9 @@ import { GameServerCreateDTOTypeEnum } from '@takaro/apiclient'; import { hasPermission } from 'hooks/useHasPermission'; export const Route = createFileRoute('/_auth/_global/gameservers/create/')({ - beforeLoad: ({ context }) => { - if (!hasPermission(context.auth.session, ['MANAGE_GAMESERVERS'])) { + beforeLoad: async ({ context }) => { + const session = await context.auth.getSession(); + if (!hasPermission(session, ['MANAGE_GAMESERVERS'])) { throw redirect({ to: '/forbidden' }); } }, diff --git a/packages/web-main/src/routes/_auth/_global/gameservers.tsx b/packages/web-main/src/routes/_auth/_global/gameservers.tsx index b87dcaa5ab..731f4a9d81 100644 --- a/packages/web-main/src/routes/_auth/_global/gameservers.tsx +++ b/packages/web-main/src/routes/_auth/_global/gameservers.tsx @@ -11,8 +11,9 @@ import { Fragment } from 'react'; import { PERMISSIONS } from '@takaro/apiclient'; export const Route = createFileRoute('/_auth/_global/gameservers')({ - beforeLoad: ({ context }) => { - if (!hasPermission(context.auth.session, ['READ_GAMESERVERS'])) { + beforeLoad: async ({ context }) => { + const session = await context.auth.getSession(); + if (!hasPermission(session, ['READ_GAMESERVERS'])) { throw redirect({ to: '/forbidden' }); } }, diff --git a/packages/web-main/src/routes/_auth/_global/gameservers.update.$gameServerId.tsx b/packages/web-main/src/routes/_auth/_global/gameservers.update.$gameServerId.tsx index bff4afb2e6..8e19d4639e 100644 --- a/packages/web-main/src/routes/_auth/_global/gameservers.update.$gameServerId.tsx +++ b/packages/web-main/src/routes/_auth/_global/gameservers.update.$gameServerId.tsx @@ -8,8 +8,9 @@ import { IFormInputs } from './-gameservers/validationSchema'; import { hasPermission } from 'hooks/useHasPermission'; export const Route = createFileRoute('/_auth/_global/gameservers/update/$gameServerId')({ - beforeLoad: ({ context }) => { - if (!hasPermission(context.auth.session, ['MANAGE_GAMESERVERS'])) { + beforeLoad: async ({ context }) => { + const session = await context.auth.getSession(); + if (!hasPermission(session, ['MANAGE_GAMESERVERS'])) { throw redirect({ to: '/forbidden' }); } }, diff --git a/packages/web-main/src/routes/_auth/_global/index.tsx b/packages/web-main/src/routes/_auth/_global/index.tsx index 40bb2a5e75..29be253884 100644 --- a/packages/web-main/src/routes/_auth/_global/index.tsx +++ b/packages/web-main/src/routes/_auth/_global/index.tsx @@ -4,17 +4,14 @@ import { createFileRoute, redirect } from '@tanstack/react-router'; import { getUserPermissions, hasPermission } from 'hooks/useHasPermission'; export const Route = createFileRoute('/_auth/_global/')({ - beforeLoad: ({ context, location }) => { - if (context.auth.isAuthenticated === false) { - throw redirect({ to: '/login', search: { redirect: location.pathname } }); - } - - if (hasPermission(context.auth.session, [PERMISSIONS.ReadEvents])) { + beforeLoad: async ({ context }) => { + const session = await context.auth.getSession(); + if (hasPermission(session, [PERMISSIONS.ReadEvents])) { throw redirect({ to: '/dashboard' }); } /* if user has no permissions at all, so can't see any page, redirect to forbidden */ - if (getUserPermissions(context.auth.session).length === 0) { + if (getUserPermissions(session).length === 0) { throw redirect({ to: '/forbidden' }); } }, diff --git a/packages/web-main/src/routes/_auth/_global/modules.$moduleId.copy.tsx b/packages/web-main/src/routes/_auth/_global/modules.$moduleId.copy.tsx index 8437898856..e1a66b4288 100644 --- a/packages/web-main/src/routes/_auth/_global/modules.$moduleId.copy.tsx +++ b/packages/web-main/src/routes/_auth/_global/modules.$moduleId.copy.tsx @@ -7,8 +7,9 @@ import { useState } from 'react'; import { CopyModuleForm } from 'components/CopyModuleForm'; export const Route = createFileRoute('/_auth/_global/modules/$moduleId/copy')({ - beforeLoad: ({ context }) => { - if (!hasPermission(context.auth.session, ['MANAGE_MODULES'])) { + beforeLoad: async ({ context }) => { + const session = await context.auth.getSession(); + if (!hasPermission(session, ['MANAGE_MODULES'])) { throw redirect({ to: '/forbidden' }); } }, diff --git a/packages/web-main/src/routes/_auth/_global/modules.$moduleId.update.tsx b/packages/web-main/src/routes/_auth/_global/modules.$moduleId.update.tsx index b35015629e..d7e78ee0f4 100644 --- a/packages/web-main/src/routes/_auth/_global/modules.$moduleId.update.tsx +++ b/packages/web-main/src/routes/_auth/_global/modules.$moduleId.update.tsx @@ -5,8 +5,9 @@ import { createFileRoute, redirect } from '@tanstack/react-router'; import { hasPermission } from 'hooks/useHasPermission'; export const Route = createFileRoute('/_auth/_global/modules/$moduleId/update')({ - beforeLoad: ({ context }) => { - if (!hasPermission(context.auth.session, ['MANAGE_MODULES'])) { + beforeLoad: async ({ context }) => { + const session = await context.auth.getSession(); + if (!hasPermission(session, ['MANAGE_MODULES'])) { throw redirect({ to: '/forbidden' }); } }, diff --git a/packages/web-main/src/routes/_auth/_global/modules.$moduleId.view.tsx b/packages/web-main/src/routes/_auth/_global/modules.$moduleId.view.tsx index ebd3234151..bfb0d68cba 100644 --- a/packages/web-main/src/routes/_auth/_global/modules.$moduleId.view.tsx +++ b/packages/web-main/src/routes/_auth/_global/modules.$moduleId.view.tsx @@ -5,8 +5,9 @@ import { createFileRoute, redirect } from '@tanstack/react-router'; import { hasPermission } from 'hooks/useHasPermission'; export const Route = createFileRoute('/_auth/_global/modules/$moduleId/view')({ - beforeLoad: ({ context }) => { - if (!hasPermission(context.auth.session, ['READ_MODULES'])) { + beforeLoad: async ({ context }) => { + const session = await context.auth.getSession(); + if (!hasPermission(session, ['READ_MODULES'])) { throw redirect({ to: '/forbidden' }); } }, diff --git a/packages/web-main/src/routes/_auth/_global/modules.create.import.tsx b/packages/web-main/src/routes/_auth/_global/modules.create.import.tsx index 4bc5bc219d..e1034e9904 100644 --- a/packages/web-main/src/routes/_auth/_global/modules.create.import.tsx +++ b/packages/web-main/src/routes/_auth/_global/modules.create.import.tsx @@ -4,8 +4,9 @@ import { ModuleImportForm, IFormInputs } from './-modules/ModuleImportForm'; import { hasPermission } from 'hooks/useHasPermission'; export const Route = createFileRoute('/_auth/_global/modules/create/import')({ - beforeLoad: ({ context }) => { - if (!hasPermission(context.auth.session, ['MANAGE_MODULES'])) { + beforeLoad: async ({ context }) => { + const session = await context.auth.getSession(); + if (!hasPermission(session, ['MANAGE_MODULES'])) { throw redirect({ to: '/forbidden' }); } }, diff --git a/packages/web-main/src/routes/_auth/_global/modules.create.index.tsx b/packages/web-main/src/routes/_auth/_global/modules.create.index.tsx index 34cfca6c47..5c66144b0c 100644 --- a/packages/web-main/src/routes/_auth/_global/modules.create.index.tsx +++ b/packages/web-main/src/routes/_auth/_global/modules.create.index.tsx @@ -4,8 +4,9 @@ import { createFileRoute, redirect } from '@tanstack/react-router'; import { hasPermission } from 'hooks/useHasPermission'; export const Route = createFileRoute('/_auth/_global/modules/create/')({ - beforeLoad: ({ context }) => { - if (!hasPermission(context.auth.session, ['MANAGE_MODULES'])) { + beforeLoad: async ({ context }) => { + const session = await context.auth.getSession(); + if (!hasPermission(session, ['MANAGE_MODULES'])) { throw redirect({ to: '/forbidden' }); } }, diff --git a/packages/web-main/src/routes/_auth/_global/modules.tsx b/packages/web-main/src/routes/_auth/_global/modules.tsx index b4c9710ace..04b0094e11 100644 --- a/packages/web-main/src/routes/_auth/_global/modules.tsx +++ b/packages/web-main/src/routes/_auth/_global/modules.tsx @@ -20,8 +20,9 @@ const SubText = styled.p` `; export const Route = createFileRoute('/_auth/_global/modules')({ - beforeLoad: ({ context }) => { - if (!hasPermission(context.auth.session, [PERMISSIONS.ReadModules])) { + beforeLoad: async ({ context }) => { + const session = await context.auth.getSession(); + if (!hasPermission(session, [PERMISSIONS.ReadModules])) { throw redirect({ to: '/forbidden' }); } }, diff --git a/packages/web-main/src/routes/_auth/_global/player.$playerId.tsx b/packages/web-main/src/routes/_auth/_global/player.$playerId.tsx index 90dd9486ee..55361a8a6b 100644 --- a/packages/web-main/src/routes/_auth/_global/player.$playerId.tsx +++ b/packages/web-main/src/routes/_auth/_global/player.$playerId.tsx @@ -8,8 +8,9 @@ import { ErrorBoundary } from 'components/ErrorBoundary'; import { hasPermission } from 'hooks/useHasPermission'; export const Route = createFileRoute('/_auth/_global/player/$playerId')({ - beforeLoad: ({ context }) => { - if (!hasPermission(context.auth.session, ['READ_PLAYERS'])) { + beforeLoad: async ({ context }) => { + const session = await context.auth.getSession(); + if (!hasPermission(session, ['READ_PLAYERS'])) { throw redirect({ to: '/forbidden' }); } }, diff --git a/packages/web-main/src/routes/_auth/_global/players.tsx b/packages/web-main/src/routes/_auth/_global/players.tsx index 5cd384b75a..b34185acba 100644 --- a/packages/web-main/src/routes/_auth/_global/players.tsx +++ b/packages/web-main/src/routes/_auth/_global/players.tsx @@ -40,8 +40,9 @@ export const StyledDialogBody = styled(Dialog.Body)` } `; export const Route = createFileRoute('/_auth/_global/players')({ - beforeLoad: ({ context }) => { - if (!hasPermission(context.auth.session, ['READ_PLAYERS'])) { + beforeLoad: async ({ context }) => { + const session = await context.auth.getSession(); + if (!hasPermission(session, ['READ_PLAYERS'])) { throw redirect({ to: '/forbidden' }); } }, diff --git a/packages/web-main/src/routes/_auth/_global/roles.create.tsx b/packages/web-main/src/routes/_auth/_global/roles.create.tsx index c4955f3852..438704bcad 100644 --- a/packages/web-main/src/routes/_auth/_global/roles.create.tsx +++ b/packages/web-main/src/routes/_auth/_global/roles.create.tsx @@ -8,8 +8,9 @@ import { RoleForm, IFormInputs } from './-roles/RoleCreateUpdateForm'; import { hasPermission } from 'hooks/useHasPermission'; export const Route = createFileRoute('/_auth/_global/roles/create')({ - beforeLoad: ({ context }) => { - if (!hasPermission(context.auth.session, ['MANAGE_ROLES'])) { + beforeLoad: async ({ context }) => { + const session = await context.auth.getSession(); + if (!hasPermission(session, ['MANAGE_ROLES'])) { throw redirect({ to: '/forbidden' }); } }, diff --git a/packages/web-main/src/routes/_auth/_global/roles.tsx b/packages/web-main/src/routes/_auth/_global/roles.tsx index f0e186d706..0f04f53f18 100644 --- a/packages/web-main/src/routes/_auth/_global/roles.tsx +++ b/packages/web-main/src/routes/_auth/_global/roles.tsx @@ -9,8 +9,9 @@ import { useInfiniteQuery } from '@tanstack/react-query'; import { InfiniteScroll } from '@takaro/lib-components'; export const Route = createFileRoute('/_auth/_global/roles')({ - beforeLoad: ({ context }) => { - if (!hasPermission(context.auth.session, ['READ_ROLES'])) { + beforeLoad: async ({ context }) => { + const session = await context.auth.getSession(); + if (!hasPermission(session, ['READ_ROLES'])) { throw redirect({ to: '/forbidden' }); } }, diff --git a/packages/web-main/src/routes/_auth/_global/roles.update.$roleId.tsx b/packages/web-main/src/routes/_auth/_global/roles.update.$roleId.tsx index 2acb7797d4..f416e7ba17 100644 --- a/packages/web-main/src/routes/_auth/_global/roles.update.$roleId.tsx +++ b/packages/web-main/src/routes/_auth/_global/roles.update.$roleId.tsx @@ -7,8 +7,9 @@ import { createFileRoute, redirect, useNavigate } from '@tanstack/react-router'; import { hasPermission } from 'hooks/useHasPermission'; export const Route = createFileRoute('/_auth/_global/roles/update/$roleId')({ - beforeLoad: ({ context }) => { - if (!hasPermission(context.auth.session, ['READ_ROLES', 'MANAGE_ROLES'])) { + beforeLoad: async ({ context }) => { + const session = await context.auth.getSession(); + if (!hasPermission(session, ['READ_ROLES', 'MANAGE_ROLES'])) { throw redirect({ to: '/forbidden' }); } }, diff --git a/packages/web-main/src/routes/_auth/_global/roles.view.$roleId.tsx b/packages/web-main/src/routes/_auth/_global/roles.view.$roleId.tsx index c103fc360e..e415b0e34f 100644 --- a/packages/web-main/src/routes/_auth/_global/roles.view.$roleId.tsx +++ b/packages/web-main/src/routes/_auth/_global/roles.view.$roleId.tsx @@ -5,8 +5,9 @@ import { createFileRoute, redirect } from '@tanstack/react-router'; import { hasPermission } from 'hooks/useHasPermission'; export const Route = createFileRoute('/_auth/_global/roles/view/$roleId')({ - beforeLoad: ({ context }) => { - if (!hasPermission(context.auth.session, ['READ_ROLES'])) { + beforeLoad: async ({ context }) => { + const session = await context.auth.getSession(); + if (!hasPermission(session, ['READ_ROLES'])) { throw redirect({ to: '/forbidden' }); } }, diff --git a/packages/web-main/src/routes/_auth/_global/settings.tsx b/packages/web-main/src/routes/_auth/_global/settings.tsx index b8eaa39cde..99e808b8bf 100644 --- a/packages/web-main/src/routes/_auth/_global/settings.tsx +++ b/packages/web-main/src/routes/_auth/_global/settings.tsx @@ -4,8 +4,9 @@ import { ErrorBoundary } from 'components/ErrorBoundary'; import { hasPermission } from 'hooks/useHasPermission'; export const Route = createFileRoute('/_auth/_global/settings')({ - beforeLoad: ({ context }) => { - if (!hasPermission(context.auth.session, ['READ_SETTINGS'])) { + beforeLoad: async ({ context }) => { + const session = await context.auth.getSession(); + if (!hasPermission(session, ['READ_SETTINGS'])) { throw redirect({ to: '/forbidden' }); } }, diff --git a/packages/web-main/src/routes/_auth/_global/settings/-discord/LoginDiscordCard.tsx b/packages/web-main/src/routes/_auth/_global/settings/-discord/LoginDiscordCard.tsx index 2edb50a821..9a6c6eeb27 100644 --- a/packages/web-main/src/routes/_auth/_global/settings/-discord/LoginDiscordCard.tsx +++ b/packages/web-main/src/routes/_auth/_global/settings/-discord/LoginDiscordCard.tsx @@ -1,8 +1,8 @@ import { FC } from 'react'; -import { useAuth } from 'hooks/useAuth'; import { Card, Button, styled, useTheme } from '@takaro/lib-components'; import { FaDiscord as DiscordIcon } from 'react-icons/fa'; import { getConfigVar } from 'util/getConfigVar'; +import { useSession } from 'hooks/useSession'; const Body = styled.div` display: flex; @@ -12,10 +12,10 @@ const Body = styled.div` `; export const LoginDiscordCard: FC = () => { - const { session } = useAuth(); + const { session } = useSession(); const { colors } = useTheme(); - const hasLinkedDiscord = !!session?.discordId; + const hasLinkedDiscord = !!session.discordId; return ( diff --git a/packages/web-main/src/routes/_auth/_global/settings/gameservers.tsx b/packages/web-main/src/routes/_auth/_global/settings/gameservers.tsx index ea7b0667a2..05388c3628 100644 --- a/packages/web-main/src/routes/_auth/_global/settings/gameservers.tsx +++ b/packages/web-main/src/routes/_auth/_global/settings/gameservers.tsx @@ -11,8 +11,9 @@ import { hasPermission, useHasPermission } from 'hooks/useHasPermission'; import { createFileRoute, redirect } from '@tanstack/react-router'; export const Route = createFileRoute('/_auth/_global/settings/gameservers')({ - beforeLoad: ({ context }) => { - if (!hasPermission(context.auth.session, ['READ_SETTINGS'])) { + beforeLoad: async ({ context }) => { + const session = await context.auth.getSession(); + if (!hasPermission(session, ['READ_SETTINGS'])) { throw redirect({ to: '/forbidden' }); } }, diff --git a/packages/web-main/src/routes/_auth/_global/user.$userId.role.assign.tsx b/packages/web-main/src/routes/_auth/_global/user.$userId.role.assign.tsx index 84f4a8f3b7..6445bd2ef8 100644 --- a/packages/web-main/src/routes/_auth/_global/user.$userId.role.assign.tsx +++ b/packages/web-main/src/routes/_auth/_global/user.$userId.role.assign.tsx @@ -36,8 +36,9 @@ const roleAssignValidationSchema = z.object({ type IFormInputs = z.infer; export const Route = createFileRoute('/_auth/_global/user/$userId/role/assign')({ - beforeLoad: ({ context }) => { - if (!hasPermission(context.auth.session, ['READ_ROLES', 'MANAGE_ROLES', 'READ_USERS', 'MANAGE_USERS'])) { + beforeLoad: async ({ context }) => { + const session = await context.auth.getSession(); + if (!hasPermission(session, ['READ_ROLES', 'MANAGE_ROLES', 'READ_USERS', 'MANAGE_USERS'])) { throw redirect({ to: '/forbidden' }); } }, diff --git a/packages/web-main/src/routes/_auth/_global/user.$userId.tsx b/packages/web-main/src/routes/_auth/_global/user.$userId.tsx index f13fedfa91..01338d2679 100644 --- a/packages/web-main/src/routes/_auth/_global/user.$userId.tsx +++ b/packages/web-main/src/routes/_auth/_global/user.$userId.tsx @@ -10,8 +10,9 @@ import { hasPermission } from 'hooks/useHasPermission'; import { useQuery } from '@tanstack/react-query'; export const Route = createFileRoute('/_auth/_global/user/$userId')({ - beforeLoad: ({ context }) => { - if (!hasPermission(context.auth.session, ['READ_USERS'])) { + beforeLoad: async ({ context }) => { + const session = await context.auth.getSession(); + if (!hasPermission(session, ['READ_USERS'])) { throw redirect({ to: '/forbidden' }); } }, diff --git a/packages/web-main/src/routes/_auth/_global/users.tsx b/packages/web-main/src/routes/_auth/_global/users.tsx index 0b6ad79a7c..9225abbfef 100644 --- a/packages/web-main/src/routes/_auth/_global/users.tsx +++ b/packages/web-main/src/routes/_auth/_global/users.tsx @@ -26,8 +26,9 @@ import { createFileRoute, useNavigate, Link, redirect } from '@tanstack/react-ro import { useQuery } from '@tanstack/react-query'; export const Route = createFileRoute('/_auth/_global/users')({ - beforeLoad: ({ context }) => { - if (!hasPermission(context.auth.session, ['READ_USERS'])) { + beforeLoad: async ({ context }) => { + const session = await context.auth.getSession(); + if (!hasPermission(session, ['READ_USERS'])) { throw redirect({ to: '/forbidden' }); } }, diff --git a/packages/web-main/src/routes/_auth/_global/variables.create.tsx b/packages/web-main/src/routes/_auth/_global/variables.create.tsx index 1c7e159005..1a0bae05e2 100644 --- a/packages/web-main/src/routes/_auth/_global/variables.create.tsx +++ b/packages/web-main/src/routes/_auth/_global/variables.create.tsx @@ -5,8 +5,9 @@ import { hasPermission } from 'hooks/useHasPermission'; import { useEffect } from 'react'; export const Route = createFileRoute('/_auth/_global/variables/create')({ - beforeLoad: ({ context }) => { - if (!hasPermission(context.auth.session, ['READ_VARIABLES', 'MANAGE_VARIABLES'])) { + beforeLoad: async ({ context }) => { + const session = await context.auth.getSession(); + if (!hasPermission(session, ['READ_VARIABLES', 'MANAGE_VARIABLES'])) { throw redirect({ to: '/forbidden' }); } }, diff --git a/packages/web-main/src/routes/_auth/_global/variables.tsx b/packages/web-main/src/routes/_auth/_global/variables.tsx index 5e8cd6f595..0663b9aaab 100644 --- a/packages/web-main/src/routes/_auth/_global/variables.tsx +++ b/packages/web-main/src/routes/_auth/_global/variables.tsx @@ -22,8 +22,9 @@ import { useQuery } from '@tanstack/react-query'; import { hasPermission } from 'hooks/useHasPermission'; export const Route = createFileRoute('/_auth/_global/variables')({ - beforeLoad: ({ context }) => { - if (!hasPermission(context.auth.session, ['READ_VARIABLES'])) { + beforeLoad: async ({ context }) => { + const session = await context.auth.getSession(); + if (!hasPermission(session, ['READ_VARIABLES'])) { throw redirect({ to: '/forbidden' }); } }, diff --git a/packages/web-main/src/routes/_auth/_global/variables.update.$variableId.tsx b/packages/web-main/src/routes/_auth/_global/variables.update.$variableId.tsx index 137253cd10..150f1e018a 100644 --- a/packages/web-main/src/routes/_auth/_global/variables.update.$variableId.tsx +++ b/packages/web-main/src/routes/_auth/_global/variables.update.$variableId.tsx @@ -9,8 +9,9 @@ import { hasPermission } from 'hooks/useHasPermission'; import { useEffect } from 'react'; export const Route = createFileRoute('/_auth/_global/variables/update/$variableId')({ - beforeLoad: ({ context }) => { - if (!hasPermission(context.auth.session, ['READ_VARIABLES', 'MANAGE_VARIABLES'])) { + beforeLoad: async ({ context }) => { + const session = await context.auth.getSession(); + if (!hasPermission(session, ['READ_VARIABLES', 'MANAGE_VARIABLES'])) { throw redirect({ to: '/forbidden' }); } }, diff --git a/packages/web-main/src/routes/_auth/gameserver.$gameServerId.tsx b/packages/web-main/src/routes/_auth/gameserver.$gameServerId.tsx index ac0df963d0..e72d39147c 100644 --- a/packages/web-main/src/routes/_auth/gameserver.$gameServerId.tsx +++ b/packages/web-main/src/routes/_auth/gameserver.$gameServerId.tsx @@ -5,8 +5,9 @@ import { gameServerQueryOptions } from 'queries/gameserver'; import { BaseLayout } from 'components/BaseLayout'; export const Route = createFileRoute('/_auth/gameserver/$gameServerId')({ - beforeLoad: ({ context }) => { - if (!hasPermission(context.auth.session, ['READ_GAMESERVERS'])) { + beforeLoad: async ({ context }) => { + const session = await context.auth.getSession(); + if (!hasPermission(session, ['READ_GAMESERVERS'])) { throw redirect({ to: '/forbidden' }); } }, diff --git a/packages/web-main/src/routes/_auth/gameserver.$gameServerId/dashboard.tsx b/packages/web-main/src/routes/_auth/gameserver.$gameServerId/dashboard.tsx index 48bb3566d1..10e66850c6 100644 --- a/packages/web-main/src/routes/_auth/gameserver.$gameServerId/dashboard.tsx +++ b/packages/web-main/src/routes/_auth/gameserver.$gameServerId/dashboard.tsx @@ -6,8 +6,9 @@ import { hasPermission } from 'hooks/useHasPermission'; import { PERMISSIONS } from '@takaro/apiclient'; export const Route = createFileRoute('/_auth/gameserver/$gameServerId/dashboard')({ - beforeLoad: ({ context }) => { - if (!hasPermission(context.auth.session, [PERMISSIONS.ReadGameservers])) { + beforeLoad: async ({ context }) => { + const session = await context.auth.getSession(); + if (!hasPermission(session, [PERMISSIONS.ReadGameservers])) { throw redirect({ to: '/forbidden' }); } }, diff --git a/packages/web-main/src/routes/_auth/gameserver.$gameServerId/modules.$moduleId.install.tsx b/packages/web-main/src/routes/_auth/gameserver.$gameServerId/modules.$moduleId.install.tsx index a8186d36fb..283578eb38 100644 --- a/packages/web-main/src/routes/_auth/gameserver.$gameServerId/modules.$moduleId.install.tsx +++ b/packages/web-main/src/routes/_auth/gameserver.$gameServerId/modules.$moduleId.install.tsx @@ -5,8 +5,9 @@ import { InstallModuleForm } from './-InstallModuleForm'; import { hasPermission } from 'hooks/useHasPermission'; export const Route = createFileRoute('/_auth/gameserver/$gameServerId/modules/$moduleId/install')({ - beforeLoad: ({ context }) => { - if (!hasPermission(context.auth.session, ['MANAGE_MODULES'])) { + beforeLoad: async ({ context }) => { + const session = await context.auth.getSession(); + if (!hasPermission(session, ['MANAGE_MODULES'])) { throw redirect({ to: '/forbidden' }); } }, diff --git a/packages/web-main/src/routes/_auth/gameserver.$gameServerId/modules.$moduleId.install.view.tsx b/packages/web-main/src/routes/_auth/gameserver.$gameServerId/modules.$moduleId.install.view.tsx index 7b22aecfcb..316b764a16 100644 --- a/packages/web-main/src/routes/_auth/gameserver.$gameServerId/modules.$moduleId.install.view.tsx +++ b/packages/web-main/src/routes/_auth/gameserver.$gameServerId/modules.$moduleId.install.view.tsx @@ -6,8 +6,9 @@ import { hasPermission } from 'hooks/useHasPermission'; import { PERMISSIONS } from '@takaro/apiclient'; export const Route = createFileRoute('/_auth/gameserver/$gameServerId/modules/$moduleId/install/view')({ - beforeLoad: ({ context }) => { - if (!hasPermission(context.auth.session, [PERMISSIONS.ReadModules])) { + beforeLoad: async ({ context }) => { + const session = await context.auth.getSession(); + if (!hasPermission(session, [PERMISSIONS.ReadModules])) { throw redirect({ to: '/forbidden' }); } }, diff --git a/packages/web-main/src/routes/_auth/gameserver.$gameServerId/modules.tsx b/packages/web-main/src/routes/_auth/gameserver.$gameServerId/modules.tsx index acc0df7878..7a1c983391 100644 --- a/packages/web-main/src/routes/_auth/gameserver.$gameServerId/modules.tsx +++ b/packages/web-main/src/routes/_auth/gameserver.$gameServerId/modules.tsx @@ -13,8 +13,9 @@ const SubHeader = styled.h2` `; export const Route = createFileRoute('/_auth/gameserver/$gameServerId/modules')({ - beforeLoad: ({ context }) => { - if (!hasPermission(context.auth.session, [PERMISSIONS.ReadModules])) { + beforeLoad: async ({ context }) => { + const session = await context.auth.getSession(); + if (!hasPermission(session, [PERMISSIONS.ReadModules])) { throw redirect({ to: '/forbidden' }); } }, diff --git a/packages/web-main/src/routes/_auth/gameserver.$gameServerId/settings.tsx b/packages/web-main/src/routes/_auth/gameserver.$gameServerId/settings.tsx index 14962d4bdd..7fc4272335 100644 --- a/packages/web-main/src/routes/_auth/gameserver.$gameServerId/settings.tsx +++ b/packages/web-main/src/routes/_auth/gameserver.$gameServerId/settings.tsx @@ -15,8 +15,9 @@ import { createFileRoute, redirect } from '@tanstack/react-router'; import { useQueries } from '@tanstack/react-query'; export const Route = createFileRoute('/_auth/gameserver/$gameServerId/settings')({ - beforeLoad: ({ context }) => { - if (!hasPermission(context.auth.session, [PERMISSIONS.ReadSettings])) { + beforeLoad: async ({ context }) => { + const session = await context.auth.getSession(); + if (!hasPermission(session, [PERMISSIONS.ReadSettings])) { throw redirect({ to: '/forbidden' }); } }, diff --git a/packages/web-main/src/routes/studio.$moduleId.tsx b/packages/web-main/src/routes/studio.$moduleId.tsx index ed784456b6..b6ca4e3a93 100644 --- a/packages/web-main/src/routes/studio.$moduleId.tsx +++ b/packages/web-main/src/routes/studio.$moduleId.tsx @@ -9,6 +9,7 @@ import { StudioInner } from './-studio/StudioInner'; import { hasPermission } from 'hooks/useHasPermission'; import { FileMap, FileType, StudioProvider } from './-studio/useStudioStore'; import { z } from 'zod'; +import { getApiClient } from 'util/getApiClient'; const Flex = styled.div` display: flex; @@ -27,12 +28,13 @@ const LoadingContainer = styled.div` `; export const Route = createFileRoute('/studio/$moduleId')({ - beforeLoad: ({ context, location }) => { - if (context.auth.isAuthenticated === false) { - throw redirect({ to: '/login', search: { redirect: location.pathname } }); - } - - if (!hasPermission(context.auth.session, ['MANAGE_MODULES'])) { + beforeLoad: async ({}) => { + try { + const me = (await getApiClient().user.userControllerMe()).data.data; + if (!hasPermission(me.user, ['MANAGE_MODULES'])) { + throw redirect({ to: '/forbidden' }); + } + } catch { throw redirect({ to: '/forbidden' }); } },