Removing frontend code

This commit is contained in:
Dario Ghunney Ware 2025-08-05 18:00:29 +01:00
parent 2591a3070d
commit 51dfade4bc
6 changed files with 26 additions and 197 deletions

View File

@ -1,59 +1,14 @@
// JWT Management Utility // Authentication utility for cookie-based JWT
window.JWTManager = { window.JWTManager = {
JWT_STORAGE_KEY: 'stirling_jwt', // Check if user is authenticated (simplified for cookie-based auth)
// Store JWT token in localStorage
storeToken: function(token) {
if (token) {
localStorage.setItem(this.JWT_STORAGE_KEY, token);
}
},
// Get JWT token from localStorage
getToken: function() {
return localStorage.getItem(this.JWT_STORAGE_KEY);
},
// Remove JWT token from localStorage
removeToken: function() {
localStorage.removeItem(this.JWT_STORAGE_KEY);
},
// Extract JWT from Authorization header in response
extractTokenFromResponse: function(response) {
const authHeader = response.headers.get('Authorization');
if (authHeader && authHeader.startsWith('Bearer ')) {
const token = authHeader.substring(7); // Remove 'Bearer ' prefix
this.storeToken(token);
return token;
}
return null;
},
// Check if user is authenticated (has valid JWT)
isAuthenticated: function() { isAuthenticated: function() {
const token = this.getToken(); // With cookie-based JWT, we rely on server-side validation
if (!token) return false; // This is a simplified check - actual authentication status is determined server-side
return document.cookie.includes('stirling_jwt=');
try {
// Basic JWT expiration check (decode payload)
const payload = JSON.parse(atob(token.split('.')[1]));
const now = Date.now() / 1000;
return payload.exp > now;
} catch (error) {
console.warn('Invalid JWT token:', error);
this.removeToken();
return false;
}
}, },
// Logout - remove token and redirect to login // Logout - clear cookies and redirect to login
logout: function() { logout: function() {
this.removeToken();
// Clear all possible token storage locations
localStorage.removeItem(this.JWT_STORAGE_KEY);
sessionStorage.removeItem(this.JWT_STORAGE_KEY);
// Clear JWT cookie manually (fallback) // Clear JWT cookie manually (fallback)
document.cookie = 'stirling_jwt=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT; SameSite=None; Secure'; document.cookie = 'stirling_jwt=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT; SameSite=None; Secure';
@ -101,20 +56,12 @@ window.fetchWithCsrf = async function(url, options = {}) {
fetchOptions.headers['X-XSRF-TOKEN'] = csrfToken; fetchOptions.headers['X-XSRF-TOKEN'] = csrfToken;
} }
// Add JWT token to Authorization header if available // Always include credentials to send JWT cookies
const jwtToken = window.JWTManager.getToken(); fetchOptions.credentials = 'include';
if (jwtToken) {
fetchOptions.headers['Authorization'] = `Bearer ${jwtToken}`;
// Include credentials when JWT is enabled
fetchOptions.credentials = 'include';
}
// Make the request // Make the request
const response = await fetch(url, fetchOptions); const response = await fetch(url, fetchOptions);
// Extract JWT from response if present
window.JWTManager.extractTokenFromResponse(response);
// Handle 401 responses (unauthorized) // Handle 401 responses (unauthorized)
if (response.status === 401) { if (response.status === 401) {
console.warn('Authentication failed, redirecting to login'); console.warn('Authentication failed, redirecting to login');

View File

@ -1,14 +1,14 @@
// JWT Initialization Script // JWT Authentication Management Script
// This script handles JWT token extraction during OAuth/Login flows and initializes the JWT manager // This script handles cookie-based JWT authentication and page access control
(function() { (function() {
// Extract JWT token from URL parameters (for OAuth redirects) // Clean up JWT token from URL parameters after OAuth/Login flows
function extractTokenFromUrl() { function cleanupTokenFromUrl() {
const urlParams = new URLSearchParams(window.location.search); const urlParams = new URLSearchParams(window.location.search);
const token = urlParams.get('jwt') || urlParams.get('token'); const hasToken = urlParams.get('jwt') || urlParams.get('token');
if (token) { if (hasToken) {
window.JWTManager.storeToken(token);
// Clean up URL by removing token parameter // Clean up URL by removing token parameter
// Token should now be set as cookie by server
urlParams.delete('jwt'); urlParams.delete('jwt');
urlParams.delete('token'); urlParams.delete('token');
const newUrl = window.location.pathname + (urlParams.toString() ? '?' + urlParams.toString() : ''); const newUrl = window.location.pathname + (urlParams.toString() ? '?' + urlParams.toString() : '');
@ -16,35 +16,16 @@
} }
} }
// Extract JWT token from cookie on page load (fallback)
function extractTokenFromCookie() {
const cookieValue = document.cookie
.split('; ')
.find(row => row.startsWith('stirling_jwt='))
?.split('=')[1];
if (cookieValue) {
window.JWTManager.storeToken(cookieValue);
// Clear the cookie since we're using localStorage with consistent SameSite policy
document.cookie = 'stirling_jwt=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT; SameSite=None; Secure';
}
}
// Initialize JWT handling when page loads // Initialize JWT handling when page loads
function initializeJWT() { function initializeJWT() {
// Try to extract token from URL first (OAuth flow) // Clean up any JWT tokens from URL (OAuth flow)
extractTokenFromUrl(); cleanupTokenFromUrl();
// If no token in URL, try cookie (login flow) // Check if user is authenticated via cookie
if (!window.JWTManager.getToken()) {
extractTokenFromCookie();
}
// Check if user is authenticated
if (window.JWTManager.isAuthenticated()) { if (window.JWTManager.isAuthenticated()) {
console.log('User is authenticated with JWT'); console.log('User is authenticated with JWT cookie');
} else { } else {
console.log('User is not authenticated or token expired'); console.log('User is not authenticated');
// Only redirect to login if we're not already on login/register pages // Only redirect to login if we're not already on login/register pages
const currentPath = window.location.pathname; const currentPath = window.location.pathname;
const currentSearch = window.location.search; const currentSearch = window.location.search;
@ -63,23 +44,11 @@
} }
} }
// Override form submissions to include JWT // No form enhancement needed for cookie-based JWT
// Cookies are automatically sent with form submissions
function enhanceFormSubmissions() { function enhanceFormSubmissions() {
// Override form submit for login forms // Cookie-based JWT is automatically included in form submissions
document.addEventListener('submit', function(event) { // No additional processing needed
const form = event.target;
// Add JWT to form data if available
const jwtToken = window.JWTManager.getToken();
if (jwtToken && form.method && form.method.toLowerCase() !== 'get') {
// Create a hidden input for JWT
const jwtInput = document.createElement('input');
jwtInput.type = 'hidden';
jwtInput.name = 'jwt';
jwtInput.value = jwtToken;
form.appendChild(jwtInput);
}
});
} }
// Add logout functionality to logout buttons // Add logout functionality to logout buttons
@ -99,12 +68,10 @@
if (document.readyState === 'loading') { if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
initializeJWT(); initializeJWT();
enhanceFormSubmissions();
enhanceLogoutButtons(); enhanceLogoutButtons();
}); });
} else { } else {
initializeJWT(); initializeJWT();
enhanceFormSubmissions();
enhanceLogoutButtons(); enhanceLogoutButtons();
} }

View File

@ -8,10 +8,8 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn; import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager; import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@ -183,7 +181,6 @@ public class SecurityConfiguration {
.csrfTokenRequestHandler(requestHandler)); .csrfTokenRequestHandler(requestHandler));
} }
// Configure session management based on JWT setting
http.sessionManagement( http.sessionManagement(
sessionManagement -> { sessionManagement -> {
if (v2Enabled) { if (v2Enabled) {
@ -346,12 +343,6 @@ public class SecurityConfiguration {
return http.build(); return http.build();
} }
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration)
throws Exception {
return configuration.getAuthenticationManager();
}
public DaoAuthenticationProvider daoAuthenticationProvider() { public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider(userDetailsService); DaoAuthenticationProvider provider = new DaoAuthenticationProvider(userDetailsService);
provider.setPasswordEncoder(passwordEncoder()); provider.setPasswordEncoder(passwordEncoder());

View File

@ -1,5 +1,6 @@
package stirling.software.proprietary.security.service; package stirling.software.proprietary.security.service;
import jakarta.annotation.PostConstruct;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
@ -19,7 +20,7 @@ import java.util.Base64;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache; import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager; import org.springframework.cache.CacheManager;
@ -27,11 +28,6 @@ import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.caffeine.CaffeineCache; import org.springframework.cache.caffeine.CaffeineCache;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import stirling.software.common.configuration.InstallationPathConfig; import stirling.software.common.configuration.InstallationPathConfig;
import stirling.software.common.model.ApplicationProperties; import stirling.software.common.model.ApplicationProperties;
import stirling.software.proprietary.security.model.JwtVerificationKey; import stirling.software.proprietary.security.model.JwtVerificationKey;
@ -240,29 +236,4 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface {
KeyFactory keyFactory = KeyFactory.getInstance("RSA"); KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(keySpec); return keyFactory.generatePublic(keySpec);
} }
@Override
public PublicKey getPublicKey(String keyId) {
try {
JwtVerificationKey verifyingKey =
verifyingKeyCache.get(keyId, JwtVerificationKey.class);
if (verifyingKey == null) {
return null;
}
return decodePublicKey(verifyingKey.getVerifyingKey());
} catch (Exception e) {
log.error("Failed to get public key for keyId: {}", keyId, e);
return null;
}
}
@Override
public PrivateKey getPrivateKey(String keyId) {
try {
return loadPrivateKey(keyId);
} catch (Exception e) {
log.error("Failed to get private key for keyId: {}", keyId, e);
return null;
}
}
} }

View File

@ -2,13 +2,11 @@ package stirling.software.proprietary.security.service;
import java.security.KeyPair; import java.security.KeyPair;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey; import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidKeySpecException;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import stirling.software.proprietary.security.model.JwtVerificationKey; import stirling.software.proprietary.security.model.JwtVerificationKey;
public interface KeyPersistenceServiceInterface { public interface KeyPersistenceServiceInterface {
@ -27,8 +25,4 @@ public interface KeyPersistenceServiceInterface {
PublicKey decodePublicKey(String encodedKey) PublicKey decodePublicKey(String encodedKey)
throws NoSuchAlgorithmException, InvalidKeySpecException; throws NoSuchAlgorithmException, InvalidKeySpecException;
PublicKey getPublicKey(String keyId);
PrivateKey getPrivateKey(String keyId);
} }

View File

@ -198,45 +198,4 @@ class KeyPersistenceServiceInterfaceTest {
assertNotNull(result.getVerifyingKey()); assertNotNull(result.getVerifyingKey());
} }
} }
@Test
void testGetPublicKey() throws Exception {
String keyId = "test-key-public";
String publicKeyBase64 = Base64.getEncoder().encodeToString(testKeyPair.getPublic().getEncoded());
JwtVerificationKey signingKey = new JwtVerificationKey(keyId, publicKeyBase64);
try (MockedStatic<InstallationPathConfig> mockedStatic = mockStatic(InstallationPathConfig.class)) {
mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString());
keyPersistenceService = new KeyPersistenceService(applicationProperties, cacheManager);
// Add the key to cache for testing
var cache = cacheManager.getCache("verifyingKeys");
cache.put(keyId, signingKey);
var result = keyPersistenceService.getPublicKey(keyId);
assertNotNull(result);
assertEquals(testKeyPair.getPublic().getAlgorithm(), result.getAlgorithm());
}
}
@Test
void testGetPrivateKey() throws Exception {
String keyId = "test-key-private";
String privateKeyBase64 = Base64.getEncoder().encodeToString(testKeyPair.getPrivate().getEncoded());
Path keyFile = tempDir.resolve(keyId + ".key");
Files.writeString(keyFile, privateKeyBase64);
try (MockedStatic<InstallationPathConfig> mockedStatic = mockStatic(InstallationPathConfig.class)) {
mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString());
keyPersistenceService = new KeyPersistenceService(applicationProperties, cacheManager);
var result = keyPersistenceService.getPrivateKey(keyId);
assertNotNull(result);
assertEquals(testKeyPair.getPrivate().getAlgorithm(), result.getAlgorithm());
}
}
} }