Session Management in Vue
March 9, 2025 · AI Conversations
### 1️⃣ **Using Local Storage (Recommended for Persistence)**
Local storage retains data even if the page is refreshed.
// After successful login
localStorage.setItem('user', JSON.stringify(response.data.user));
// Retrieve session data
const user = JSON.parse(localStorage.getItem('user'));
// Remove session data on logout
localStorage.removeItem('user');
### 2️⃣ **Using Vuex or Pinia (State Management)**
If you are using **Vuex** or **Pinia**, store session data globally.
#### ✅ **Vuex (if used in your project)**
// store.js
import { createStore } from 'vuex';
export default createStore({
state: {
user: null
},
mutations: {
setUser(state, user) {
state.user = user;
}
},
actions: {
login({ commit }, userData) {
commit('setUser', userData);
localStorage.setItem('user', JSON.stringify(userData)); // Optional for persistence
},
logout({ commit }) {
commit('setUser', null);
localStorage.removeItem('user');
}
}
});
#### ✅ **Pinia (Alternative)**
// stores/auth.js
import { defineStore } from 'pinia';
export const useAuthStore = defineStore('auth', {
state: () => ({
user: JSON.parse(localStorage.getItem('user')) || null
}),
actions: {
setUser(user) {
this.user = user;
localStorage.setItem('user', JSON.stringify(user));
},
logout() {
this.user = null;
localStorage.removeItem('user');
}
}
});
### 3️⃣ **Using Cookies (For Laravel Session)**
If you want to keep the session in sync with Laravel, use **cookies**.
#### ✅ **Set Session with Cookies in Laravel API**
Modify the login method in `AuthController.php`:
return response()->json([
'user' => $user
])->cookie('session_token', $token, 60); // 60 minutes expiry
#### ✅ **Retrieve Session in Vue**
Use `js-cookie` to store session data.
import Cookies from 'js-cookie';
// Set session
Cookies.set('session_token', response.data.token, { expires: 1 }); // 1 day
// Get session
const sessionToken = Cookies.get('session_token');
// Remove session on logout
Cookies.remove('session_token');
—
### 🔥 **Best Approach for Your Laravel + Inertia.js Setup**
Since Inertia uses **Laravel sessions**, you can simply store authentication data globally:
#### ✅ **After Login**
// Call Laravel API for login
axios.post('/login', credentials)
.then(response => {
localStorage.setItem('user', JSON.stringify(response.data.user));
Inertia.visit('/dashboard'); // Redirect to dashboard
});
#### ✅ **On Logout**
axios.post('/logout').then(() => {
localStorage.removeItem('user');
Inertia.visit('/login'); // Redirect to login
});
—
### 🎯 **Final Recommendation**
– If you need persistence, use **LocalStorage**.
– If you want centralized state management, use **Pinia or Vuex**.
– If you rely on Laravel sessions, use **cookies**.
Which method fits your setup best? 🚀
—
## ✅ **1. Install Pinia (if not installed)**
yarn add pinia
—
## ✅ **2. Create an Authentication Store (`auth.js`)**
Use **localStorage** to persist user data across refreshes.
// stores/auth.js
import { defineStore } from 'pinia';
export const useAuthStore = defineStore('auth', {
state: () => ({
user: JSON.parse(localStorage.getItem('user')) || null
}),
actions: {
setUser(user) {
this.user = user;
localStorage.setItem('user', JSON.stringify(user));
},
logout() {
this.user = null;
localStorage.removeItem('user');
}
}
});
—
## ✅ **3. Register Pinia in `main.js`**
Modify your `main.js` to use Pinia:
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';
const app = createApp(App);
app.use(createPinia());
app.mount('#app');
—
## ✅ **4. Use the Store in Components**
### **🔹 In Login Component**
<script setup>
import { ref } from 'vue';
import { useAuthStore } from '@/stores/auth';
import axios from 'axios';
import { useRouter } from 'vue-router';
const authStore = useAuthStore();
const router = useRouter();
const email = ref('');
const password = ref('');
const login = async () => {
try {
const response = await axios.post('/login', { email: email.value, password: password.value });
authStore.setUser(response.data.user);
router.push('/dashboard'); // Redirect after login
} catch (error) {
console.error('Login failed', error);
}
};
</script>
<template>
<form @submit.prevent="login">
<input v-model="email" type="email" placeholder="Email">
<input v-model="password" type="password" placeholder="Password">
<button type="submit">Login</button>
</form>
</template>
—
### **🔹 In Logout Component**
<script setup>
import { useAuthStore } from '@/stores/auth';
import axios from 'axios';
import { useRouter } from 'vue-router';
const authStore = useAuthStore();
const router = useRouter();
const logout = async () => {
try {
await axios.post('/logout');
authStore.logout();
router.push('/login'); // Redirect to login
} catch (error) {
console.error('Logout failed', error);
}
};
</script>
<template>
<button @click="logout">Logout</button>
</template>
—
### **🔹 In Navbar (Show Login/Logout Based on Session)**
<script setup>
import { useAuthStore } from '@/stores/auth';
const authStore = useAuthStore();
</script>
<template>
<nav>
<span v-if="authStore.user">Welcome, {{ authStore.user.name }}</span>
<router-link v-if="!authStore.user" to="/login">Login</router-link>
<button v-if="authStore.user" @click="authStore.logout">Logout</button>
</nav>
</template>
—
## 🎯 **How It Works**
– When the user logs in, `authStore.setUser(user)` saves data in **Pinia** and **localStorage**.
– On refresh, `state: () => ({ user: JSON.parse(localStorage.getItem('user')) || null })` loads data from **localStorage**.
– On logout, `authStore.logout()` clears **Pinia** state and **localStorage**.
—
### 🔥 **Final Thoughts**
This setup ensures:
✔️ **Session persists across refreshes**
✔️ **State is managed centrally with Pinia**
✔️ **Easy access to user data across components**
Would you like to add automatic token handling for API authentication? 🚀
—
## ✅ **1. Update the `auth.js` Store**
Store both **user data** and **token** in **localStorage**.
// stores/auth.js
import { defineStore } from 'pinia';
export const useAuthStore = defineStore('auth', {
state: () => ({
user: JSON.parse(localStorage.getItem('user')) || null,
token: localStorage.getItem('token') || null,
}),
actions: {
setUser(user, token) {
this.user = user;
this.token = token;
localStorage.setItem('user', JSON.stringify(user));
localStorage.setItem('token', token);
},
logout() {
this.user = null;
this.token = null;
localStorage.removeItem('user');
localStorage.removeItem('token');
}
}
});
—
## ✅ **2. Modify the Login Component**
After logging in, store both **user** and **token**.
<script setup>
import { ref } from 'vue';
import { useAuthStore } from '@/stores/auth';
import axios from 'axios';
import { useRouter } from 'vue-router';
const authStore = useAuthStore();
const router = useRouter();
const email = ref('');
const password = ref('');
const login = async () => {
try {
const response = await axios.post('/login', { email: email.value, password: password.value });
// Store user & token
authStore.setUser(response.data.user, response.data.token);
// Redirect to dashboard
router.push('/dashboard');
} catch (error) {
console.error('Login failed', error);
}
};
</script>
<template>
<form @submit.prevent="login">
<input v-model="email" type="email" placeholder="Email">
<input v-model="password" type="password" placeholder="Password">
<button type="submit">Login</button>
</form>
</template>
—
## ✅ **3. Modify the Logout Component**
Clear **user** and **token** when logging out.
<script setup>
import { useAuthStore } from '@/stores/auth';
import axios from 'axios';
import { useRouter } from 'vue-router';
const authStore = useAuthStore();
const router = useRouter();
const logout = async () => {
try {
await axios.post('/logout', {}, {
headers: {
Authorization: `Bearer ${authStore.token}` // Send token for logout request
}
});
authStore.logout();
router.push('/login');
} catch (error) {
console.error('Logout failed', error);
}
};
</script>
<template>
<button @click="logout">Logout</button>
</template>
—
## ✅ **4. Send Token with Every API Request**
You need to attach the token to **all API requests** for authenticated routes.
### **🔹 Create an Axios Interceptor (`axios.js`)**
import axios from 'axios';
import { useAuthStore } from '@/stores/auth';
const api = axios.create({
baseURL: 'https://your-api-url.com', // Replace with your API URL
});
// Attach token to every request
api.interceptors.request.use((config) => {
const authStore = useAuthStore();
if (authStore.token) {
config.headers.Authorization = `Bearer ${authStore.token}`;
}
return config;
}, (error) => {
return Promise.reject(error);
});
export default api;
### **🔹 Use `api.js` Instead of `axios`**
Now, in all your components, **replace** `axios` with `api` to automatically include the token.
import api from '@/axios.js';
const fetchData = async () => {
try {
const response = await api.get('/protected-route');
console.log(response.data);
} catch (error) {
console.error(error);
}
};
—
## ✅ **5. Protect Routes (Optional)**
To prevent unauthorized users from accessing protected pages, **check the token before entering routes**.
// router.js
import { createRouter, createWebHistory } from 'vue-router';
import { useAuthStore } from '@/stores/auth';
import Dashboard from '@/views/Dashboard.vue';
import Login from '@/views/Login.vue';
const routes = [
{ path: '/login', component: Login },
{
path: '/dashboard',
component: Dashboard,
meta: { requiresAuth: true }
}
];
const router = createRouter({
history: createWebHistory(),
routes
});
router.beforeEach((to, from, next) => {
const authStore = useAuthStore();
if (to.meta.requiresAuth && !authStore.token) {
next('/login'); // Redirect if not logged in
} else {
next();
}
});
export default router;
—
## 🎯 **How It Works**
✔️ **User logs in** → Stores `user` & `token` in **Pinia + localStorage**
✔️ **Page refresh** → Reloads `user` & `token` from **localStorage**
✔️ **All API requests** automatically include **Authorization: Bearer token**
✔️ **Logout** clears session from **Pinia + localStorage**
✔️ **Routes are protected** based on `token`
—
## 🚀 **Final Thoughts**
– This setup ensures seamless **session persistence** even after refresh.
– API requests **automatically include the token**.
– **Unauthorized users** are redirected to `/login`.
Would you like to add **auto-refresh for tokens**? 🔄🔥
type null is not assignanle to string
—
### ✅ **Fix in TypeScript (`auth.ts`)**
Modify the store to handle this properly:
// stores/auth.ts
import { defineStore } from 'pinia';
interface User {
id: number;
name: string;
email: string;
}
export const useAuthStore = defineStore('auth', {
state: () => ({
user: localStorage.getItem('user') ? (JSON.parse(localStorage.getItem('user')!) as User) : null,
token: localStorage.getItem('token') || null,
}),
actions: {
setUser(user: User, token: string) {
this.user = user;
this.token = token;
localStorage.setItem('user', JSON.stringify(user));
localStorage.setItem('token', token);
},
logout() {
this.user = null;
this.token = null;
localStorage.removeItem('user');
localStorage.removeItem('token');
}
}
});
—
### 🔹 **Changes & Fixes**
1. **Defined an `interface User`** → Ensures `user` has a specific structure.
2. **Used `JSON.parse(localStorage.getItem('user')!)`** → `!` tells TypeScript that `getItem('user')` won't be `null` when parsing.
3. **Explicitly cast `as User`** → Helps TypeScript understand the parsed object.
—
### ✅ **Alternative Without `!` Operator**
user: (() => {
const storedUser = localStorage.getItem('user');
return storedUser ? (JSON.parse(storedUser) as User) : null;
})(),
This avoids using `!` and ensures safe handling of `null`.
—
### 🚀 **Final Thoughts**
✔️ **Fixes TypeScript error**
✔️ **Ensures correct type safety**
✔️ **Handles `null` properly without breaking the app**
Let me know if you need further refinements! 🚀
—
## ✅ **1. Create Two Axios Instances**
– **Public API (`publicApi.js`)** → For login, registration, etc. (No token required).
– **Protected API (`api.js`)** → For authenticated routes (Token required).
—
### **🔹 `publicApi.js` (For Public Routes)**
Use this for login, registration, etc.
import axios from 'axios';
const publicApi = axios.create({
baseURL: 'https://your-api-url.com', // Replace with your API URL
});
export default publicApi;
—
### **🔹 `api.js` (For Protected Routes)**
Use this only for routes requiring authentication.
import axios from 'axios';
import { useAuthStore } from '@/stores/auth';
const api = axios.create({
baseURL: 'https://your-api-url.com',
});
// Attach token to protected requests
api.interceptors.request.use(
(config) => {
const authStore = useAuthStore();
if (authStore.token) {
config.headers.Authorization = `Bearer ${authStore.token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
export default api;
—
## ✅ **2. Use Correct API in Components**
– **Public API** (`publicApi.js`) for login, signup.
– **Protected API** (`api.js`) for authenticated requests.
### **🔹 Login Component (Uses `publicApi.js`)**
<script setup>
import { ref } from 'vue';
import { useAuthStore } from '@/stores/auth';
import publicApi from '@/publicApi';
import { useRouter } from 'vue-router';
const authStore = useAuthStore();
const router = useRouter();
const email = ref('');
const password = ref('');
const login = async () => {
try {
const response = await publicApi.post('/login', {
email: email.value,
password: password.value
});
authStore.setUser(response.data.user, response.data.token);
router.push('/dashboard');
} catch (error) {
console.error('Login failed', error);
}
};
</script>
—
### **🔹 Fetch Protected Data (Uses `api.js`)**
<script setup>
import { ref, onMounted } from 'vue';
import api from '@/api';
const userData = ref(null);
const fetchData = async () => {
try {
const response = await api.get('/user-data'); // Protected route
userData.value = response.data;
} catch (error) {
console.error('Error fetching user data', error);
}
};
onMounted(fetchData);
</script>
—
## ✅ **Final Setup**
✔️ **Public API (`publicApi.js`)** → Used for login, register, etc.
✔️ **Protected API (`api.js`)** → Used for authenticated requests.
✔️ **Components use correct API** → Prevents unnecessary token headers.
Let me know if you need further refinements! 🚀
—
## ✅ **Fix 1: Ensure Middleware is Applied**
Make sure your `logout` method is protected by authentication middleware.
Modify your `routes/api.php`:
use AppHttpControllersAPIAuthAuthController;
use IlluminateSupportFacadesRoute;
Route::post('/register', [AuthController::class, 'register']);
Route::post('/verify-otp', [AuthController::class, 'verifyOtp']);
Route::post('/login', [AuthController::class, 'login']);
Route::middleware('auth:sanctum')->group(function () {
Route::post('/logout', [AuthController::class, 'logout']);
});
🔹 **Explanation**:
– `auth:sanctum` ensures only authenticated users can access `logout`.
– If a user is not logged in, `logout` won’t be accessible.
—
## ✅ **Fix 2: Use `auth()` Helper Instead of `$request->user()`**
Modify `logout()` to explicitly get the authenticated user:
use IlluminateSupportFacadesAuth;
public function logout(Request $request)
{
$user = Auth::user();
if (!$user) {
return response()->json(['message' => 'User not authenticated'], 401);
}
$user->currentAccessToken()->delete();
return response()->json(['message' => 'Logged out successfully'], 200);
}
🔹 **Why?**
– `Auth::user()` explicitly retrieves the authenticated user.
– If `null`, it returns an **unauthenticated** response instead of causing a fatal error.
—
## ✅ **Fix 3: Ensure Bearer Token is Sent in Logout Request**
Your logout request must include the `Authorization: Bearer ` header.
Example **Axios Request in Vue.js**:
import api from '@/api'; // Protected API instance
async function logout() {
try {
await api.post('/logout');
localStorage.removeItem('user');
localStorage.removeItem('token');
} catch (error) {
console.error('Logout failed:', error);
}
}
—
## 🚀 **Final Steps**
✔ **Ensure `auth:sanctum` middleware is applied in `routes/api.php`**
✔ **Use `Auth::user()` instead of `$request->user()`**
✔ **Send the Bearer token in the logout request**
This should resolve the issue! Let me know if you need further clarifications. 🚀