feat(auth): enhance logout and login flow with return URL handling

This commit is contained in:
2025-04-25 14:41:58 -04:00
parent 2c09be4465
commit e394c346ae
5 changed files with 53 additions and 11 deletions

View File

@@ -55,7 +55,7 @@ export function useClient() {
return client(originalRequest); return client(originalRequest);
} catch (refreshError) { } catch (refreshError) {
console.error('Token refresh failed, logging out user:', refreshError); console.error('Token refresh failed, logging out user:', refreshError);
await authStore.logout(); await authStore.logout('/login');
throw refreshError; throw refreshError;
} }
} }

View File

@@ -94,6 +94,7 @@ const routes = [
name: 'login', name: 'login',
component: LoginView, component: LoginView,
meta: { notAuthenticated: true }, meta: { notAuthenticated: true },
props: (route) => ({ returnUrl: route.query.returnUrl || '/landing' })
}, },
{ {
path: '/profile', path: '/profile',
@@ -119,11 +120,20 @@ router.beforeEach((to, from, next) => {
const authStore = useAuthStore(); const authStore = useAuthStore();
if (to.matched.some((record) => record.meta.requiresAuth)) { if (to.matched.some((record) => record.meta.requiresAuth)) {
if (!authStore.isAuthenticated) next({ name: 'login' }); if (!authStore.isAuthenticated) {
next({
name: 'login',
query: { returnUrl: to.fullPath }
});
} else {
next();
}
} else if (to.matched.some((record) => record.meta.notAuthenticated)) { } else if (to.matched.some((record) => record.meta.notAuthenticated)) {
if (authStore.isAuthenticated) next({ name: 'landing' }); if (authStore.isAuthenticated) next({ name: 'landing' });
else next();
} else {
next();
} }
next();
}); });
export default router; export default router;

View File

@@ -72,7 +72,7 @@ export const useAuthStore = defineStore(
// Clear any other auth-related data if needed // Clear any other auth-related data if needed
} }
async function logout() { async function logout(redirectTo = '/landing') {
try { try {
// Optionally call logout endpoint if you have one // Optionally call logout endpoint if you have one
// await clientApi.post('api/users/logout'); // await clientApi.post('api/users/logout');
@@ -80,7 +80,7 @@ export const useAuthStore = defineStore(
console.error('Logout failed:', error); console.error('Logout failed:', error);
} finally { } finally {
cleanTokens(); cleanTokens();
await router.push('/'); await router.push(redirectTo);
} }
} }
@@ -201,8 +201,15 @@ export const useAuthStore = defineStore(
// Only clear tokens and session storage after a failed refresh attempt // Only clear tokens and session storage after a failed refresh attempt
cleanTokens(); cleanTokens();
// Force a redirect to the login page // Get the current route to use as returnUrl
await router.push('/login'); const currentRoute = router.currentRoute.value;
const returnUrl = currentRoute.fullPath;
// Force a redirect to the login page with returnUrl
await router.push({
name: 'login',
query: { returnUrl }
});
throw error; throw error;
} }

View File

@@ -3,16 +3,30 @@ import {ref} from 'vue';
import {GoogleLogin} from "vue3-google-login"; import {GoogleLogin} from "vue3-google-login";
import {useAuthStore} from '@/stores/authStore.js'; import {useAuthStore} from '@/stores/authStore.js';
import {useI18n} from 'vue-i18n'; import {useI18n} from 'vue-i18n';
import {useRouter} from 'vue-router';
const {t} = useI18n(); const {t} = useI18n();
const router = useRouter();
const authStore = useAuthStore(); const authStore = useAuthStore();
const errorSnackBar = ref(false); const errorSnackBar = ref(false);
const props = defineProps({
returnUrl: {
type: String,
default: '/landing'
}
});
async function googleCallback(token) { async function googleCallback(token) {
const response = await authStore.loginWithGoogle(JSON.stringify(token)); try {
if (response !== true) { const response = await authStore.loginWithGoogle(JSON.stringify(token));
if (response === true) {
await router.push(props.returnUrl);
} else {
errorSnackBar.value = true;
}
} catch (error) {
console.error('Login failed:', error);
errorSnackBar.value = true; errorSnackBar.value = true;
} }
} }

View File

@@ -4,9 +4,12 @@ import {useAuthStore} from "@/stores/authStore.js";
import {useCreatorProfileStore} from "@/stores/creatorProfileStore.js"; import {useCreatorProfileStore} from "@/stores/creatorProfileStore.js";
import {useUserProfileStore} from "@/stores/userProfileStore.js"; import {useUserProfileStore} from "@/stores/userProfileStore.js";
import {useLanguageStore} from "@/stores/languageStore.js"; import {useLanguageStore} from "@/stores/languageStore.js";
import {useRouter, useRoute} from 'vue-router';
const {locale, t} = useI18n(); const {locale, t} = useI18n();
const languageStore = useLanguageStore(); const languageStore = useLanguageStore();
const router = useRouter();
const route = useRoute();
const userProfileStore = useUserProfileStore(); const userProfileStore = useUserProfileStore();
const creatorProfileStore = useCreatorProfileStore(); const creatorProfileStore = useCreatorProfileStore();
@@ -18,6 +21,14 @@ function toggleLanguage() {
const nextIndex = (currentIndex + 1) % languages.length; const nextIndex = (currentIndex + 1) % languages.length;
languageStore.setLocale(languages[nextIndex]); languageStore.setLocale(languages[nextIndex]);
} }
function handleLogout() {
// Check if current route requires authentication
const requiresAuth = route.matched.some(record => record.meta.requiresAuth);
// If on a protected page, redirect to landing, otherwise stay on current page
const redirectTo = requiresAuth ? '/landing' : route.fullPath;
authStore.logout(redirectTo);
}
</script> </script>
<template> <template>
@@ -85,7 +96,7 @@ function toggleLanguage() {
</template> </template>
<div v-else> <div v-else>
<button class="menu-item-action" <button class="menu-item-action"
@click="authStore.logout"> @click="handleLogout">
<i class="mdi mdi-logout"></i> <i class="mdi mdi-logout"></i>
<span class="label">{{ t('sidebar.signOut') }}</span> <span class="label">{{ t('sidebar.signOut') }}</span>
</button> </button>