307 lines
7.8 KiB
JavaScript
307 lines
7.8 KiB
JavaScript
// Cookie utility functions for authentication
|
|
const COOKIE_OPTIONS = {
|
|
path: '/',
|
|
secure: window.location.protocol === 'https:',
|
|
sameSite: 'Lax',
|
|
maxAge: 7 * 24 * 60 * 60 // 7 days in seconds
|
|
}
|
|
|
|
/**
|
|
* Set a cookie with authentication data
|
|
* @param {string} name - Cookie name
|
|
* @param {string} value - Cookie value
|
|
* @param {Object} options - Cookie options
|
|
*/
|
|
export const setCookie = (name, value, options = {}) => {
|
|
try {
|
|
if (!name || value === undefined || value === null) {
|
|
console.warn('Invalid cookie parameters:', { name, value })
|
|
return false
|
|
}
|
|
|
|
const cookieOptions = { ...COOKIE_OPTIONS, ...options }
|
|
|
|
let cookieString = `${name}=${encodeURIComponent(value)}`
|
|
|
|
if (cookieOptions.maxAge) {
|
|
cookieString += `; Max-Age=${cookieOptions.maxAge}`
|
|
}
|
|
|
|
if (cookieOptions.path) {
|
|
cookieString += `; Path=${cookieOptions.path}`
|
|
}
|
|
|
|
if (cookieOptions.domain) {
|
|
cookieString += `; Domain=${cookieOptions.domain}`
|
|
}
|
|
|
|
if (cookieOptions.secure) {
|
|
cookieString += '; Secure'
|
|
}
|
|
|
|
if (cookieOptions.sameSite) {
|
|
cookieString += `; SameSite=${cookieOptions.sameSite}`
|
|
}
|
|
|
|
if (cookieOptions.httpOnly) {
|
|
cookieString += '; HttpOnly'
|
|
}
|
|
|
|
document.cookie = cookieString
|
|
return true
|
|
} catch (error) {
|
|
console.error('Error setting cookie:', error)
|
|
return false
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get a cookie value by name
|
|
* @param {string} name - Cookie name
|
|
* @returns {string|null} - Cookie value or null if not found
|
|
*/
|
|
export const getCookie = (name) => {
|
|
try {
|
|
if (!name) {
|
|
console.warn('Cookie name is required')
|
|
return null
|
|
}
|
|
|
|
const nameEQ = name + '='
|
|
const cookies = document.cookie.split(';')
|
|
|
|
for (let i = 0; i < cookies.length; i++) {
|
|
let cookie = cookies[i]
|
|
while (cookie.charAt(0) === ' ') {
|
|
cookie = cookie.substring(1, cookie.length)
|
|
}
|
|
if (cookie.indexOf(nameEQ) === 0) {
|
|
return decodeURIComponent(
|
|
cookie.substring(nameEQ.length, cookie.length)
|
|
)
|
|
}
|
|
}
|
|
return null
|
|
} catch (error) {
|
|
console.error('Error getting cookie:', error)
|
|
return null
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove a cookie by setting it to expire in the past
|
|
* @param {string} name - Cookie name
|
|
* @param {Object} options - Cookie options (path and domain must match when setting)
|
|
*/
|
|
export const removeCookie = (name, options = {}) => {
|
|
try {
|
|
if (!name) {
|
|
console.warn('Cookie name is required for removal')
|
|
return false
|
|
}
|
|
|
|
const cookieOptions = { ...COOKIE_OPTIONS, ...options }
|
|
setCookie(name, '', { ...cookieOptions, maxAge: -1 })
|
|
return true
|
|
} catch (error) {
|
|
console.error('Error removing cookie:', error)
|
|
return false
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if cookies are enabled in the browser
|
|
* @returns {boolean} - True if cookies are enabled
|
|
*/
|
|
export const areCookiesEnabled = () => {
|
|
try {
|
|
setCookie('test', 'test')
|
|
const enabled = getCookie('test') === 'test'
|
|
removeCookie('test')
|
|
return enabled
|
|
} catch (e) {
|
|
console.error('Error checking if cookies are enabled:', e)
|
|
return false
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if authentication cookies are expired and clean them up if needed
|
|
* @returns {boolean} - True if cookies are valid and not expired
|
|
*/
|
|
export const validateAuthCookies = () => {
|
|
try {
|
|
const { token, expiresAt, user } = getAuthCookies()
|
|
|
|
if (!token || !expiresAt || !user) {
|
|
return false
|
|
}
|
|
|
|
const now = new Date()
|
|
const expirationDate = new Date(expiresAt)
|
|
|
|
if (expirationDate <= now) {
|
|
// Cookies are expired, clean them up
|
|
clearAuthCookies()
|
|
return false
|
|
}
|
|
|
|
return true
|
|
} catch (error) {
|
|
console.error('Error validating auth cookies:', error)
|
|
clearAuthCookies()
|
|
return false
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if authentication cookies are about to expire (within specified minutes)
|
|
* @param {number} minutesBeforeExpiry - Minutes before expiry to consider "about to expire"
|
|
* @returns {Object} - Object with isExpiringSoon flag and timeRemaining in milliseconds
|
|
*/
|
|
export const checkAuthCookiesExpiry = (minutesBeforeExpiry = 5) => {
|
|
try {
|
|
const { token, expiresAt, user } = getAuthCookies()
|
|
|
|
if (!token || !expiresAt || !user) {
|
|
return { isExpiringSoon: false, timeRemaining: 0 }
|
|
}
|
|
|
|
const now = new Date()
|
|
const expirationDate = new Date(expiresAt)
|
|
const timeRemaining = expirationDate - now
|
|
const minutesRemaining = timeRemaining / (1000 * 60)
|
|
|
|
return {
|
|
isExpiringSoon: minutesRemaining <= minutesBeforeExpiry,
|
|
timeRemaining,
|
|
minutesRemaining: Math.floor(minutesRemaining)
|
|
}
|
|
} catch (error) {
|
|
console.error('Error checking auth cookies expiry:', error)
|
|
return { isExpiringSoon: false, timeRemaining: 0 }
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set up a listener for cookie changes to sync authentication state between tabs
|
|
* @param {Function} onAuthChange - Callback function when auth state changes
|
|
* @returns {Function} - Function to remove the listener
|
|
*/
|
|
export const setupCookieSync = (onAuthChange) => {
|
|
const handleStorageChange = (event) => {
|
|
// Check if auth-related cookies changed
|
|
if (
|
|
event.key === 'authToken' ||
|
|
event.key === 'authExpiresAt' ||
|
|
event.key === 'user'
|
|
) {
|
|
// Small delay to ensure cookies are updated
|
|
setTimeout(() => {
|
|
onAuthChange()
|
|
}, 100)
|
|
}
|
|
}
|
|
|
|
// Listen for storage events (for cross-tab communication)
|
|
window.addEventListener('storage', handleStorageChange)
|
|
|
|
// Also listen for cookie changes using a polling mechanism
|
|
let lastCookieState = document.cookie
|
|
const cookieCheckInterval = setInterval(() => {
|
|
const currentCookieState = document.cookie
|
|
if (currentCookieState !== lastCookieState) {
|
|
lastCookieState = currentCookieState
|
|
// Check if auth cookies changed
|
|
const authCookies = getAuthCookies()
|
|
if (authCookies.token || authCookies.expiresAt || authCookies.user) {
|
|
onAuthChange()
|
|
}
|
|
}
|
|
}, 1000)
|
|
|
|
// Return cleanup function
|
|
return () => {
|
|
window.removeEventListener('storage', handleStorageChange)
|
|
clearInterval(cookieCheckInterval)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set authentication cookies
|
|
* @param {Object} authData - Authentication data object
|
|
* @returns {boolean} - True if all cookies were set successfully
|
|
*/
|
|
export const setAuthCookies = (authData) => {
|
|
try {
|
|
if (!authData) {
|
|
console.warn('Auth data is required')
|
|
return false
|
|
}
|
|
|
|
let success = true
|
|
|
|
if (authData.access_token) {
|
|
success =
|
|
success &&
|
|
setCookie('authToken', authData.access_token, {
|
|
maxAge: 7 * 24 * 60 * 60
|
|
}) // 7 days
|
|
}
|
|
|
|
if (authData.expires_at) {
|
|
success =
|
|
success &&
|
|
setCookie('authExpiresAt', authData.expires_at, {
|
|
maxAge: 7 * 24 * 60 * 60
|
|
})
|
|
}
|
|
|
|
if (authData.user) {
|
|
const userObject = {
|
|
...authData.user,
|
|
access_token: undefined,
|
|
refresh_token: undefined,
|
|
id_token: undefined
|
|
}
|
|
success =
|
|
success &&
|
|
setCookie('user', JSON.stringify(userObject), {
|
|
maxAge: 7 * 24 * 60 * 60
|
|
})
|
|
}
|
|
|
|
if (!success) {
|
|
console.warn('Some cookies failed to set, clearing all auth cookies')
|
|
clearAuthCookies()
|
|
}
|
|
|
|
return success
|
|
} catch (error) {
|
|
console.error('Error setting auth cookies:', error)
|
|
clearAuthCookies()
|
|
return false
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get authentication cookies
|
|
* @returns {Object} - Object containing auth data from cookies
|
|
*/
|
|
export const getAuthCookies = () => {
|
|
return {
|
|
token: getCookie('authToken'),
|
|
expiresAt: getCookie('authExpiresAt'),
|
|
user: getCookie('user') ? JSON.parse(getCookie('user')) : null
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clear authentication cookies
|
|
*/
|
|
export const clearAuthCookies = () => {
|
|
removeCookie('authToken')
|
|
removeCookie('authExpiresAt')
|
|
removeCookie('user')
|
|
}
|