diff --git a/src/components/layout/header/AvatarMenu.tsx b/src/components/layout/header/AvatarMenu.tsx index f9d6b4d0de4..90a3e7957ec 100644 --- a/src/components/layout/header/AvatarMenu.tsx +++ b/src/components/layout/header/AvatarMenu.tsx @@ -16,6 +16,7 @@ import Link from 'next/link'; import { forwardRef } from 'react'; import { useColorScheme } from '~/hooks/use-colorscheme'; +import { env } from '~/env'; import { useBoardLink } from '../Templates/BoardLayout'; export const AvatarMenu = () => { @@ -69,7 +70,8 @@ export const AvatarMenu = () => { color="red" onClick={() => signOut({ - redirect: false, + callbackUrl: env.NEXT_PUBLIC_LOGOUT_REDIRECT_URL ?? "/", + redirect: env.NEXT_PUBLIC_LOGOUT_REDIRECT_URL != undefined, }).then(() => window.location.reload()) } > diff --git a/src/env.js b/src/env.js index 9e7aeb50e47..dd1022f7f5d 100644 --- a/src/env.js +++ b/src/env.js @@ -49,6 +49,8 @@ const env = createEnv({ DEMO_MODE: z.string().optional(), HOSTNAME: z.string().optional(), + AUTH_SESSION_EXPIRY_TIME: numberSchema, + // Authentication AUTH_PROVIDER: z .string() @@ -118,6 +120,7 @@ const env = createEnv({ .optional() .default('light'), NEXT_PUBLIC_DOCKER_HOST: z.string().optional(), + NEXT_PUBLIC_LOGOUT_REDIRECT_URL: z.string().optional() }, /** * You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g. @@ -157,6 +160,8 @@ const env = createEnv({ AUTH_OIDC_AUTO_LOGIN: process.env.AUTH_OIDC_AUTO_LOGIN, AUTH_OIDC_SCOPE_OVERWRITE: process.env.AUTH_OIDC_SCOPE_OVERWRITE, AUTH_OIDC_TIMEOUT: process.env.AUTH_OIDC_TIMEOUT, + NEXT_PUBLIC_LOGOUT_REDIRECT_URL: process.env.AUTH_OIDC_REDIRECT_LOGOUT_URL, + AUTH_SESSION_EXPIRY_TIME: process.env.AUTH_SESSION_EXPIRY_TIME, DEMO_MODE: process.env.DEMO_MODE, }, skipValidation: !!process.env.SKIP_ENV_VALIDATION, diff --git a/src/hooks/custom-session-provider.tsx b/src/hooks/custom-session-provider.tsx new file mode 100644 index 00000000000..62d9d6a90f8 --- /dev/null +++ b/src/hooks/custom-session-provider.tsx @@ -0,0 +1,27 @@ +import dayjs from 'dayjs'; +import relativeTime from 'dayjs/plugin/relativeTime'; +import { Session } from 'next-auth'; +import { SessionProvider, signIn } from 'next-auth/react'; +import { useEffect } from 'react'; + +dayjs.extend(relativeTime); + +interface CustomSessionProviderProps { + session: Session; + children: React.ReactNode; +} + +export const CustomSessionProvider = ({ session, children }: CustomSessionProviderProps) => { + //Automatically redirect to the login page after a session expires + useEffect(() => { + if (!session) return () => {}; + const timeout = setTimeout(signIn, dayjs(session?.expires).diff(new Date())); + return () => clearTimeout(timeout); + }, [session]); + + return ( + + {children} + + ); +}; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 1795ac8e1b8..e58c8d3867b 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -10,7 +10,7 @@ import utc from 'dayjs/plugin/utc'; import 'flag-icons/css/flag-icons.min.css'; import { GetServerSidePropsContext } from 'next'; import { Session } from 'next-auth'; -import { getSession, SessionProvider } from 'next-auth/react'; +import { getSession } from 'next-auth/react'; import { appWithTranslation } from 'next-i18next'; import { AppProps } from 'next/app'; import Script from 'next/script'; @@ -30,6 +30,7 @@ import { ConfigType } from '~/types/config'; import { api } from '~/utils/api'; import { colorSchemeParser } from '~/validations/user'; +import { CustomSessionProvider } from '~/hooks/custom-session-provider'; import { COOKIE_COLOR_SCHEME_KEY, COOKIE_LOCALE_KEY } from '../../data/constants'; import nextI18nextConfig from '../../next-i18next.config.js'; import '../styles/global.scss'; @@ -111,7 +112,7 @@ function App( strategy="lazyOnload" /> )} - + {(colorScheme) => ( @@ -151,7 +152,7 @@ function App( )} - + ); } diff --git a/src/server/auth.ts b/src/server/auth.ts index 38094037834..4b25603c1dc 100644 --- a/src/server/auth.ts +++ b/src/server/auth.ts @@ -10,10 +10,11 @@ import EmptyNextAuthProvider from '~/utils/empty-provider'; import { fromDate, generateSessionToken } from '~/utils/session'; import { colorSchemeParser } from '~/validations/user'; +import { env } from '~/env'; import { db } from './db'; import { users } from './db/schema'; -const sessionMaxAgeInSeconds = 30 * 24 * 60 * 60; // 30 days +const sessionMaxAgeInSeconds = env.AUTH_SESSION_EXPIRY_TIME ?? 30 * 24 * 60 * 60; // 30 days /** * Options for NextAuth.js used to configure adapters, providers, callbacks, etc.