Kritim Yantra
Jun 04, 2025
Imagine your JWT token is a concert ticket π«:
Without refresh logic, users get logged out constantly. Letβs fix that!
In .env
, set token lifetimes:
JWT_TTL=60 # Access token expires in 60 minutes (for testing)
JWT_REFRESH_TTL=10080 # Refresh token expires in 7 days (10080 minutes)
Add to routes/api.php
:
Route::post('/refresh', [AuthController::class, 'refresh']);
Update AuthController.php
:
public function refresh() {
try {
$newToken = auth()->refresh();
return response()->json([
'token' => $newToken,
'refresh_token' => $newToken // Simplified for clarity
]);
} catch (\Tymon\JWTAuth\Exceptions\TokenInvalidException $e) {
return response()->json(['error' => 'Invalid token'], 401);
}
}
Update your Axios setup (resources/js/app.js
):
axios.interceptors.response.use(response => response, async error => {
const originalRequest = error.config;
// If token expired (401) and not already retrying
if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
try {
// Attempt token refresh
const refreshResponse = await axios.post('/api/refresh');
const newToken = refreshResponse.data.token;
// Update stored token
localStorage.setItem('jwt_token', newToken);
// Retry original request with new token
originalRequest.headers.Authorization = `Bearer ${newToken}`;
return axios(originalRequest);
} catch (refreshError) {
// Refresh failed - force logout
localStorage.removeItem('jwt_token');
window.location.href = '/login';
}
}
return Promise.reject(error);
});
Create resources/js/components/Profile.vue
:
<template>
<div class="profile-card">
<h1>π Hello, {{ user.name }}!</h1>
<div class="details">
<p><strong>π§ Email:</strong> {{ user.email }}</p>
<p><strong>π User ID:</strong> {{ user.id }}</p>
<p><strong>π Member since:</strong> {{ formattedDate }}</p>
</div>
<button @click="logout" class="logout-btn">Log Out</button>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
user: {}
};
},
computed: {
formattedDate() {
return new Date(this.user.created_at).toLocaleDateString();
}
},
async mounted() {
try {
const response = await axios.get('/api/user');
this.user = response.data;
} catch (error) {
console.error("Profile load error:", error);
}
},
methods: {
logout() {
axios.post('/api/logout');
localStorage.removeItem('jwt_token');
this.$router.push('/login');
}
}
};
</script>
<style scoped>
.profile-card {
max-width: 500px;
margin: 2rem auto;
padding: 2rem;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
background: white;
}
.details {
text-align: left;
padding: 1.5rem;
background: #f9fafb;
border-radius: 8px;
margin: 1.5rem 0;
}
.logout-btn {
background: #ef4444;
color: white;
border: none;
padding: 12px 24px;
border-radius: 8px;
cursor: pointer;
font-weight: 600;
transition: background 0.3s;
}
.logout-btn:hover {
background: #dc2626;
}
</style>
Install Vue Router:
npm install vue-router@4
Create resources/js/router.js
:
import { createRouter, createWebHistory } from 'vue-router';
import Login from './components/Login.vue';
import Profile from './components/Profile.vue';
const routes = [
{ path: '/', redirect: '/profile' },
{ path: '/login', component: Login },
{ path: '/profile', component: Profile, meta: { requiresAuth: true } }
];
const router = createRouter({
history: createWebHistory(),
routes
});
// Auth protection
router.beforeEach((to, from, next) => {
const hasToken = localStorage.getItem('jwt_token');
if (to.meta.requiresAuth && !hasToken) {
next('/login');
} else {
next();
}
});
export default router;
Update app.js
:
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
createApp(App).use(router).mount('#app');
Ensure routes/api.php
has:
Route::middleware('auth:api')->get('/user', function (Request $request) {
return $request->user();
});
/profile
β See your details! "Good authentication is like oxygen β users only notice it when itβs missing."
No comments yet. Be the first to comment!
Please log in to post a comment:
Sign in with Google