diff --git a/example.env b/example.env index 0a4a0e3..8e0b039 100644 --- a/example.env +++ b/example.env @@ -4,6 +4,7 @@ PN_WEBSITE_PUBLIC_BASE_URL=http://localhost:3000 PN_WEBSITE_PUBLIC_CDN_BASE_URL=http://pretendo.localhost:3902 PN_WEBSITE_PUBLIC_COOKIE_SECURE=false +PN_WEBSITE_PUBLIC_REDIRECT_HOSTS=localhost:3000 # Optional - Authentication PN_WEBSITE_GRPC_HOST=localhost:8123 diff --git a/nuxt.config.ts b/nuxt.config.ts index b636569..0d1871a 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -65,6 +65,7 @@ export default defineNuxtConfig({ public: { baseUrl: 'https://pretendo.network', cdnBaseUrl: 'https://r2-cdn.pretendo.cc', + redirectHosts: 'pretendo.network', hcaptchaSiteKey: '', cookieSecure: true } diff --git a/src/components/Navbar/Navbar.vue b/src/components/Navbar/Navbar.vue index a9fa664..a55655e 100644 --- a/src/components/Navbar/Navbar.vue +++ b/src/components/Navbar/Navbar.vue @@ -3,7 +3,7 @@ import { Popover } from 'reka-ui/namespaced'; const { locales, setLocale } = useI18n(); const me = useMeStore(); -const authStore = useAuthStore(); +const authUtils = useAuthUtils(); const user = computed(() => me.user); const openDropdown = ref(false); @@ -522,7 +522,7 @@ onMounted(() => { diff --git a/src/composables/useAuthUtils.ts b/src/composables/useAuthUtils.ts new file mode 100644 index 0000000..202ecfb --- /dev/null +++ b/src/composables/useAuthUtils.ts @@ -0,0 +1,62 @@ +export function getSafeRedirectUrl(input: string | null, baseUrl: string, allowedHosts: string[] = []): { url: string; external: boolean } | null { + const parsedBaseUrl = new URL(baseUrl); + if (!input) { + return null; // No or empty input + } + + let url: URL | null = null; + try { + url = new URL(input, parsedBaseUrl); + } catch { + return null; // Invalid URL input + } + + const isAllowedHost = allowedHosts.includes(url.host); + if (!isAllowedHost) { + return null; // Not in the allowed hosts + } + + // If on the same host as the website, only return the path. For internal redirects + if (url.host === parsedBaseUrl.host) { + return { + url: url.pathname + url.search, + external: false + }; + } + return { + url: url.toString(), + external: true + }; +} + +export function useAuthUtils() { + const authStore = useAuthStore(); + const route = useRoute(); + const config = useRuntimeConfig(); + const allowedRedirectHosts = computed(() => config.public.redirectHosts.split(' ').map(v => v.trim()).filter(v => v.length > 0)); + + return { + async safelyRedirectAfterLogin(inputUrl: string | null) { + const redirectUrl = getSafeRedirectUrl(inputUrl, config.public.baseUrl, allowedRedirectHosts.value); + if (!redirectUrl) { + await navigateTo('/account'); + return; + } + await navigateTo(redirectUrl.url, { + external: redirectUrl.external + }); + }, + async redirectToLogin() { + await navigateTo({ + path: '/account/login', + query: { + redirect: route.fullPath + } + }); + }, + async logout() { + authStore.logout(); + await navigateTo('/'); // Back to homepage + } + }; +} diff --git a/src/pages/account/login.vue b/src/pages/account/login.vue index 4a38a57..23d7627 100644 --- a/src/pages/account/login.vue +++ b/src/pages/account/login.vue @@ -1,8 +1,17 @@