mirror of
https://github.com/ajurna/cbwebreader.git
synced 2026-11-02 10:36:55 +00:00
Add authentication and session management improvements
Build and push image / deploy (push) Has been cancelled
Build and push image / deploy (push) Has been cancelled
Introduce navigation guards for authentication and admin access within routes. Replace localStorage usage with secure token storage via httpOnly cookies, and add token blacklisting upon logout. Enhance token refresh mechanism and session expiration handling to improve security and user experience.
This commit is contained in:
1 parent
dd5817419b
commit
e5086ec653
7 files changed
+204
-58
No files matched your search
+63
-24
@@ -3,38 +3,77 @@ import router from "@/router";
|
||||
import store from "@/store";
|
||||
import { jwtDecode } from "jwt-decode";
|
||||
|
||||
/**
|
||||
* Gets a valid access token or refreshes if needed
|
||||
* Uses a consistent 5-minute threshold for token expiration
|
||||
*/
|
||||
async function get_access_token() {
|
||||
let access = jwtDecode(store.state.jwt.access)
|
||||
let refresh = jwtDecode(store.state.jwt.refresh)
|
||||
if (access.exp - Date.now()/1000 < 5) {
|
||||
if (refresh.exp - Date.now()/1000 < 5) {
|
||||
await router.push({name: 'login'})
|
||||
return null
|
||||
} else {
|
||||
return store.dispatch('refreshToken').then(() => {return store.state.jwt.access})
|
||||
// If we don't have tokens in the store, return null
|
||||
if (!store.state.jwt || !store.state.jwt.access) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const access = jwtDecode(store.state.jwt.access);
|
||||
const now = Date.now() / 1000;
|
||||
const refreshThreshold = 300; // 5 minutes in seconds
|
||||
|
||||
// If token is about to expire, refresh it
|
||||
if (access.exp - now < refreshThreshold) {
|
||||
try {
|
||||
// Wait for the token to refresh
|
||||
await store.dispatch('refreshToken');
|
||||
return store.state.jwt.access;
|
||||
} catch (error) {
|
||||
console.error('Failed to refresh token:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return store.state.jwt.access;
|
||||
} catch (error) {
|
||||
console.error('Error decoding token:', error);
|
||||
return null;
|
||||
}
|
||||
return store.state.jwt.access
|
||||
}
|
||||
}
|
||||
|
||||
const axios_jwt = axios.create();
|
||||
|
||||
axios_jwt.interceptors.request.use(async function (config) {
|
||||
let access_token = await get_access_token().catch(() => {
|
||||
if (router.currentRoute.value.fullPath.includes('login')){
|
||||
router.push({name: 'login'})
|
||||
}else {
|
||||
router.push({name: 'login', query: { next: router.currentRoute.value.fullPath }})
|
||||
}
|
||||
// Add CSRF token to all requests if using cookies for authentication
|
||||
axios_jwt.interceptors.request.use(function(config) {
|
||||
// Get CSRF token from cookie if it exists
|
||||
const csrfToken = document.cookie
|
||||
.split('; ')
|
||||
.find(row => row.startsWith('csrftoken='))
|
||||
?.split('=')[1];
|
||||
|
||||
})
|
||||
config.headers = {
|
||||
Authorization: "Bearer " + access_token
|
||||
if (csrfToken) {
|
||||
config.headers['X-CSRFToken'] = csrfToken;
|
||||
}
|
||||
return config
|
||||
}, function (error) {
|
||||
// Do something with request error
|
||||
|
||||
return config;
|
||||
});
|
||||
|
||||
// Add JWT token to all requests
|
||||
axios_jwt.interceptors.request.use(async function (config) {
|
||||
const access_token = await get_access_token();
|
||||
|
||||
if (access_token) {
|
||||
config.headers.Authorization = "Bearer " + access_token;
|
||||
} else if (!router.currentRoute.value.fullPath.includes('login')) {
|
||||
// Only redirect if we're not already on the login page
|
||||
router.push({
|
||||
name: 'login',
|
||||
query: {
|
||||
next: router.currentRoute.value.fullPath,
|
||||
error: 'Please log in to continue'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return config;
|
||||
}, function (error) {
|
||||
return Promise.reject(error);
|
||||
});
|
||||
});
|
||||
|
||||
export default axios_jwt
|
||||
Reference in new issue
Block a user