A complete authentication flow: login, protected routes, token persistence, and automatic logout on expiry.
// stores/auth.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import api from '@/api/axios'
export const useAuthStore = defineStore('auth', () => {
const token = ref(localStorage.getItem('token'))
const user = ref(JSON.parse(localStorage.getItem('user') || 'null'))
const isAuthenticated = computed(() => !!token.value)
async function login(email, password) {
const { data } = await api.post('/login', { email, password })
token.value = data.token
user.value = data.user
localStorage.setItem('token', data.token)
localStorage.setItem('user', JSON.stringify(data.user))
}
function logout() {
token.value = null
user.value = null
localStorage.removeItem('token')
localStorage.removeItem('user')
}
return { token, user, isAuthenticated, login, logout }
}, { persist: true })
// router/index.js
import { useAuthStore } from '@/stores/auth'
router.beforeEach((to) => {
const auth = useAuthStore()
if (to.meta.requiresAuth && !auth.isAuthenticated) {
return { name: 'login', query: { redirect: to.fullPath } }
}
if (to.meta.guestOnly && auth.isAuthenticated) {
return { name: 'dashboard' }
}
})
const routes = [
{ path: '/login', component: LoginView, meta: { guestOnly: true } },
{ path: '/dashboard', component: DashboardView, meta: { requiresAuth: true } },
]
api.interceptors.request.use((config) => {
const auth = useAuthStore()
if (auth.token) {
config.headers.Authorization = `Bearer ${auth.token}`
}
return config
})
// Auto logout on 401
api.interceptors.response.use(
(res) => res,
(err) => {
if (err.response?.status === 401) {
useAuthStore().logout()
router.push('/login')
}
return Promise.reject(err)
}
)
All Comments