// src/contexts/AuthContext.js import React, { createContext, useState, useCallback, useEffect } from 'react' import axios from 'axios' import { message, Modal, notification, Progress, Button, Space } from 'antd' import PropTypes from 'prop-types' import ExclamationOctogonIcon from '../../Icons/ExclamationOctagonIcon' import InfoCircleIcon from '../../Icons/InfoCircleIcon' import config from '../../../config' import AppError from '../../App/AppError' import loglevel from 'loglevel' const logger = loglevel.getLogger('ApiServerContext') logger.setLevel(config.logLevel) const AuthContext = createContext() const AuthProvider = ({ children }) => { const [messageApi, contextHolder] = message.useMessage() const [notificationApi, notificationContextHolder] = notification.useNotification() const [authenticated, setAuthenticated] = useState(false) const [loading, setLoading] = useState(false) const [token, setToken] = useState(null) const [expiresAt, setExpiresAt] = useState(null) const [userProfile, setUserProfile] = useState(null) const [showSessionExpiredModal, setShowSessionExpiredModal] = useState(false) const [showUnauthorizedModal, setShowUnauthorizedModal] = useState(false) const [authError, setAuthError] = useState(null) const logout = useCallback((redirectUri = '/login') => { setAuthenticated(false) setToken(null) setExpiresAt(null) setUserProfile(null) window.location.href = `${config.backendUrl}/auth/logout?redirect_uri=${encodeURIComponent(redirectUri)}` }, []) // Login using query parameters const loginWithSSO = useCallback( (redirectUri = window.location.pathname + window.location.search) => { messageApi.info('Logging in with tombutcher.work') window.location.href = `${config.backendUrl}/auth/login?redirect_uri=${encodeURIComponent(redirectUri)}` }, [messageApi] ) // Function to check if the user is logged in const checkAuthStatus = useCallback(async () => { setLoading(true) setAuthError(null) try { // Make a call to your backend to check auth status const response = await axios.get(`${config.backendUrl}/auth/user`, { withCredentials: true // Important for including cookies }) if (response.status === 200 && response.data) { logger.debug('Got auth token!') setToken(response.data.access_token) setExpiresAt(response.data.expires_at) setUserProfile(response.data) } else { setAuthenticated(false) setAuthError('Failed to authenticate user.') } } catch (error) { logger.debug('Auth check failed', error) if (error.response?.status === 401) { setShowUnauthorizedModal(true) } else { setAuthError('Error connecting to authentication service.') } setAuthenticated(false) } finally { setLoading(false) } }, []) const refreshToken = useCallback(async () => { try { const response = await axios.get(`${config.backendUrl}/auth/refresh`, { withCredentials: true }) if (response.status === 200 && response.data) { setToken(response.data.access_token) setExpiresAt(response.data.expires_at) } } catch (error) { console.error('Token refresh failed', error) } }, []) const handleSessionExpiredModalOk = () => { setShowSessionExpiredModal(false) loginWithSSO() } // Initialize on component mount useEffect(() => { let intervalId const tokenRefresh = () => { if (expiresAt) { const now = new Date() const expirationDate = new Date(expiresAt) const timeRemaining = expirationDate - now if (timeRemaining <= 0) { if (authenticated == true) { setAuthenticated(false) } setShowSessionExpiredModal(true) notificationApi.destroy('token-expiration') } else { if (authenticated == false) { setAuthenticated(true) } const minutes = Math.floor(timeRemaining / 60000) const seconds = Math.floor((timeRemaining % 60000) / 1000) // Only show notification in the final minute if (minutes === 0) { const totalSeconds = 60 const remainingSeconds = totalSeconds - seconds const progress = (remainingSeconds / totalSeconds) * 100 notificationApi.info({ message: 'Session Expiring Soon', description: (