From 13e09968d928bc5e255dd3f93eca3d3e42f1b144 Mon Sep 17 00:00:00 2001 From: Meier Lukas Date: Fri, 9 Aug 2024 19:24:07 +0200 Subject: [PATCH] feat: add logout redirect url (#954) * feat: add logout redirect url * fix: format issue --- .../[locale]/_client-providers/session.tsx | 27 ++++++++++++++++--- apps/nextjs/src/app/[locale]/layout.tsx | 3 ++- .../src/components/user-avatar-menu.tsx | 6 +++++ packages/auth/env.mjs | 2 ++ turbo.json | 1 + 5 files changed, 34 insertions(+), 5 deletions(-) diff --git a/apps/nextjs/src/app/[locale]/_client-providers/session.tsx b/apps/nextjs/src/app/[locale]/_client-providers/session.tsx index a4622de94..249299bbb 100644 --- a/apps/nextjs/src/app/[locale]/_client-providers/session.tsx +++ b/apps/nextjs/src/app/[locale]/_client-providers/session.tsx @@ -1,19 +1,38 @@ "use client"; +import { createContext, useContext, useEffect } from "react"; import type { PropsWithChildren } from "react"; -import { useEffect } from "react"; import dayjs from "dayjs"; import type { Session } from "@homarr/auth"; import { SessionProvider, signIn } from "@homarr/auth/client"; -interface AuthProviderProps { +interface AuthProviderProps extends AuthContextProps { session: Session | null; } -export const AuthProvider = ({ children, session }: PropsWithChildren) => { +export const AuthProvider = ({ children, session, logoutUrl }: PropsWithChildren) => { useLoginRedirectOnSessionExpiry(session); - return {children}; + + return ( + + {children} + + ); +}; + +interface AuthContextProps { + logoutUrl: string | undefined; +} + +const AuthContext = createContext(null); + +export const useAuthContext = () => { + const context = useContext(AuthContext); + + if (!context) throw new Error("useAuthContext must be used within an AuthProvider"); + + return context; }; const useLoginRedirectOnSessionExpiry = (session: Session | null) => { diff --git a/apps/nextjs/src/app/[locale]/layout.tsx b/apps/nextjs/src/app/[locale]/layout.tsx index 7b3a8d657..223ebf9be 100644 --- a/apps/nextjs/src/app/[locale]/layout.tsx +++ b/apps/nextjs/src/app/[locale]/layout.tsx @@ -8,6 +8,7 @@ import "~/styles/scroll-area.scss"; import { ColorSchemeScript, createTheme, MantineProvider } from "@mantine/core"; +import { env } from "@homarr/auth/env.mjs"; import { auth } from "@homarr/auth/next"; import { ModalProvider } from "@homarr/modals"; import { Notifications } from "@homarr/notifications"; @@ -56,7 +57,7 @@ export default function Layout(props: { children: React.ReactNode; params: { loc const StackedProvider = composeWrappers([ async (innerProps) => { const session = await auth(); - return ; + return ; }, (innerProps) => , (innerProps) => , diff --git a/apps/nextjs/src/components/user-avatar-menu.tsx b/apps/nextjs/src/components/user-avatar-menu.tsx index f8be743e6..6ac0f4bd7 100644 --- a/apps/nextjs/src/components/user-avatar-menu.tsx +++ b/apps/nextjs/src/components/user-avatar-menu.tsx @@ -23,6 +23,7 @@ import { useScopedI18n } from "@homarr/translation/client"; import "flag-icons/css/flag-icons.min.css"; +import { useAuthContext } from "~/app/[locale]/_client-providers/session"; import { LanguageCombobox } from "./language/language-combobox"; interface UserAvatarMenuProps { @@ -40,6 +41,7 @@ export const UserAvatarMenu = ({ children }: UserAvatarMenuProps) => { const session = useSession(); const router = useRouter(); + const { logoutUrl } = useAuthContext(); const { openModal } = useModalAction(LogoutModal); const handleSignout = useCallback(async () => { @@ -48,6 +50,10 @@ export const UserAvatarMenu = ({ children }: UserAvatarMenuProps) => { }); openModal({ onTimeout: () => { + if (logoutUrl) { + window.location.assign(logoutUrl); + return; + } router.refresh(); }, }); diff --git a/packages/auth/env.mjs b/packages/auth/env.mjs index 1a15dd811..d8f7b3121 100644 --- a/packages/auth/env.mjs +++ b/packages/auth/env.mjs @@ -62,6 +62,7 @@ const authProviders = skipValidation ? [] : authProvidersSchema.parse(process.en export const env = createEnv({ server: { + AUTH_LOGOUT_REDIRECT_URL: z.string().url().optional(), AUTH_SESSION_EXPIRY_TIME: createDurationSchema("30d"), AUTH_SECRET: process.env.NODE_ENV === "production" ? z.string().min(1) : z.string().min(1).optional(), AUTH_PROVIDERS: authProvidersSchema, @@ -94,6 +95,7 @@ export const env = createEnv({ }, client: {}, runtimeEnv: { + AUTH_LOGOUT_REDIRECT_URL: process.env.AUTH_LOGOUT_REDIRECT_URL, AUTH_SESSION_EXPIRY_TIME: process.env.AUTH_SESSION_EXPIRY_TIME, AUTH_SECRET: process.env.AUTH_SECRET, AUTH_PROVIDERS: process.env.AUTH_PROVIDERS, diff --git a/turbo.json b/turbo.json index 9528e6d00..67e69d087 100644 --- a/turbo.json +++ b/turbo.json @@ -22,6 +22,7 @@ "AUTH_LDAP_USER_MAIL_ATTRIBUTE", "AUTH_LDAP_USERNAME_FILTER_EXTRA_ARG", "AUTH_OIDC_AUTO_LOGIN", + "AUTH_LOGOUT_REDIRECT_URL", "AUTH_PROVIDERS", "AUTH_SECRET", "AUTH_SESSION_EXPIRY_TIME",