diff --git a/pkg/web/static/js/api.js b/pkg/web/static/js/api.js index 3da31b4..84db0ab 100644 --- a/pkg/web/static/js/api.js +++ b/pkg/web/static/js/api.js @@ -1,6 +1,11 @@ -// API helper functions for Fail2ban UI +// API helpers for Fail2ban UI. +"use strict"; -// Add server parameter to URL +// ========================================================================= +// Server-Scoped Requests +// ========================================================================= + +// Adds the server ID to the URL if a server is selected. function withServerParam(url) { if (!currentServerId) { return url; @@ -8,7 +13,7 @@ function withServerParam(url) { return url + (url.indexOf('?') === -1 ? '?' : '&') + 'serverId=' + encodeURIComponent(currentServerId); } -// Get server headers for API requests +// Adds the server ID to the headers if a server is selected. function serverHeaders(headers) { headers = headers || {}; if (currentServerId) { @@ -16,28 +21,3 @@ function serverHeaders(headers) { } return headers; } - -// Auth-aware fetch wrapper that handles 401/403 responses -function authFetch(url, options) { - options = options || {}; - // Ensure Accept header for API requests - if (!options.headers) { - options.headers = {}; - } - if (!options.headers['Accept']) { - options.headers['Accept'] = 'application/json'; - } - - return fetch(url, options).then(function(response) { - // Handle authentication errors - if (response.status === 401 || response.status === 403) { - if (typeof handleAuthError === 'function') { - handleAuthError(response); - } - // Return a rejected promise to stop the chain - return Promise.reject(new Error('Authentication required')); - } - return response; - }); -} - diff --git a/pkg/web/static/js/auth.js b/pkg/web/static/js/auth.js index c036b92..5b0e402 100644 --- a/pkg/web/static/js/auth.js +++ b/pkg/web/static/js/auth.js @@ -1,11 +1,18 @@ -// Authentication functions for Fail2ban UI +// Auth flow for Fail2ban UI. "use strict"; +// ========================================================================= +// Global Variables +// ========================================================================= + let authEnabled = false; let isAuthenticated = false; let currentUser = null; -// Check authentication status on page load +// ========================================================================= +// Check Authentication Status +// ========================================================================= + async function checkAuthStatus() { // Both login page and main content are hidden by default // We'll show the appropriate one based on authentication status @@ -13,8 +20,6 @@ async function checkAuthStatus() { const nav = document.querySelector('nav'); const loginPage = document.getElementById('loginPage'); const footer = document.getElementById('footer'); - - // Ensure all are hidden initially to prevent flash if (loginPage) { loginPage.classList.add('hidden'); loginPage.style.display = 'none'; @@ -31,21 +36,21 @@ async function checkAuthStatus() { footer.classList.add('hidden'); footer.style.display = 'none'; } - + try { const response = await fetch('/auth/status', { headers: serverHeaders() }); - + if (!response.ok) { throw new Error('Failed to check auth status'); } - + const data = await response.json(); authEnabled = data.enabled || false; isAuthenticated = data.authenticated || false; const skipLoginPageFlag = data.skipLoginPage || false; - + if (authEnabled) { if (isAuthenticated && data.user) { // Authenticated: show main content, hide login page @@ -54,7 +59,6 @@ async function checkAuthStatus() { } else { // Not authenticated if (skipLoginPageFlag) { - // Skip login page: redirect directly to OIDC provider window.location.href = '/auth/login'; return { enabled: authEnabled, authenticated: false, user: null }; } else { @@ -66,14 +70,13 @@ async function checkAuthStatus() { // OIDC not enabled: show main content, hide login page showMainContent(); } - + return { enabled: authEnabled, authenticated: isAuthenticated, user: currentUser }; } catch (error) { console.error('Error checking auth status:', error); - // On error, check OIDC status from data attributes const oidcEnabled = document.body.getAttribute('data-oidc-enabled') === 'true'; const skipLoginPage = document.body.getAttribute('data-skip-login-page') === 'true'; - + if (oidcEnabled) { if (skipLoginPage) { window.location.href = '/auth/login'; @@ -87,80 +90,47 @@ async function checkAuthStatus() { } } -// Get current user info -async function getUserInfo() { - try { - const response = await fetch('/auth/user', { - headers: serverHeaders() - }); - - if (!response.ok) { - if (response.status === 401) { - isAuthenticated = false; - currentUser = null; - showLoginPage(); - return null; - } - throw new Error('Failed to get user info'); - } - - const data = await response.json(); - if (data.authenticated && data.user) { - currentUser = data.user; - isAuthenticated = true; - return data.user; - } - - return null; - } catch (error) { - console.error('Error getting user info:', error); - return null; - } -} +// ========================================================================= +// Handle Login and Logout +// ========================================================================= -// Handle login - redirect to login endpoint with action parameter function handleLogin() { const loginLoading = document.getElementById('loginLoading'); const loginError = document.getElementById('loginError'); const loginErrorText = document.getElementById('loginErrorText'); const loginButton = event?.target?.closest('button'); - - // Show loading state + if (loginLoading) loginLoading.classList.remove('hidden'); if (loginButton) { loginButton.disabled = true; loginButton.classList.add('opacity-75', 'cursor-not-allowed'); } - - // Hide error if shown + if (loginError) { loginError.classList.add('hidden'); if (loginErrorText) loginErrorText.textContent = ''; } - - // Redirect to login endpoint with action=redirect to trigger OIDC redirect window.location.href = '/auth/login?action=redirect'; } -// Handle logout - use direct redirect instead of fetch to avoid CORS issues function handleLogout() { - // Clear local state + // Clear authentication status and redirect to logout endpoint isAuthenticated = false; currentUser = null; - - // Direct redirect to logout endpoint (server will handle redirect to provider) - // Using window.location.href instead of fetch to avoid CORS issues with redirects window.location.href = '/auth/logout'; } -// Show login page +// ========================================================================= +// Show Different Application States (Login, Main Content, etc.) +// ========================================================================= + function showLoginPage() { const loginPage = document.getElementById('loginPage'); const mainContent = document.getElementById('mainContent'); const nav = document.querySelector('nav'); const footer = document.getElementById('footer'); - // Hide main content, nav, and footer + // Hide main content if (mainContent) { mainContent.style.display = 'none'; mainContent.classList.add('hidden'); @@ -181,7 +151,6 @@ function showLoginPage() { } } -// Show main content (when authenticated or OIDC disabled) function showMainContent() { const loginPage = document.getElementById('loginPage'); const mainContent = document.getElementById('mainContent'); @@ -194,7 +163,7 @@ function showMainContent() { loginPage.classList.add('hidden'); } - // Show main content, nav, and footer + // Show main content if (mainContent) { mainContent.style.display = 'block'; mainContent.classList.remove('hidden'); @@ -209,30 +178,9 @@ function showMainContent() { } } -// Toggle user menu dropdown -function toggleUserMenu() { - const dropdown = document.getElementById('userMenuDropdown'); - if (dropdown) { - dropdown.classList.toggle('hidden'); - } -} - -// Close user menu when clicking outside -document.addEventListener('click', function(event) { - const userMenuButton = document.getElementById('userMenuButton'); - const userMenuDropdown = document.getElementById('userMenuDropdown'); - - if (userMenuButton && userMenuDropdown && - !userMenuButton.contains(event.target) && - !userMenuDropdown.contains(event.target)) { - userMenuDropdown.classList.add('hidden'); - } -}); - -// Show authenticated UI (update header with user info) function showAuthenticatedUI() { showMainContent(); - + const userInfoContainer = document.getElementById('userInfoContainer'); const userDisplayName = document.getElementById('userDisplayName'); const userMenuDisplayName = document.getElementById('userMenuDisplayName'); @@ -240,20 +188,20 @@ function showAuthenticatedUI() { const mobileUserInfoContainer = document.getElementById('mobileUserInfoContainer'); const mobileUserDisplayName = document.getElementById('mobileUserDisplayName'); const mobileUserEmail = document.getElementById('mobileUserEmail'); - + if (userInfoContainer && currentUser) { userInfoContainer.classList.remove('hidden'); - + const displayName = currentUser.name || currentUser.username || currentUser.email; - + if (userDisplayName) { userDisplayName.textContent = displayName; } - + if (userMenuDisplayName) { userMenuDisplayName.textContent = displayName; } - + if (userMenuEmail && currentUser.email) { userMenuEmail.textContent = currentUser.email; } @@ -262,28 +210,37 @@ function showAuthenticatedUI() { // Update mobile menu if (mobileUserInfoContainer && currentUser) { mobileUserInfoContainer.classList.remove('hidden'); - + const displayName = currentUser.name || currentUser.username || currentUser.email; - + if (mobileUserDisplayName) { mobileUserDisplayName.textContent = displayName; } - + if (mobileUserEmail && currentUser.email) { mobileUserEmail.textContent = currentUser.email; } } } -// Handle 401/403 responses from API -function handleAuthError(response) { - if (response.status === 401 || response.status === 403) { - if (authEnabled) { - isAuthenticated = false; - currentUser = null; - showLoginPage(); - return true; - } +// ========================================================================= +// Helper Functions +// ========================================================================= + +function toggleUserMenu() { + const dropdown = document.getElementById('userMenuDropdown'); + if (dropdown) { + dropdown.classList.toggle('hidden'); } - return false; } + +document.addEventListener('click', function(event) { + const userMenuButton = document.getElementById('userMenuButton'); + const userMenuDropdown = document.getElementById('userMenuDropdown'); + + if (userMenuButton && userMenuDropdown && + !userMenuButton.contains(event.target) && + !userMenuDropdown.contains(event.target)) { + userMenuDropdown.classList.add('hidden'); + } +});