diff --git a/aggregator-server/internal/api/handlers/setup.go b/aggregator-server/internal/api/handlers/setup.go index ec9baaf..f142ec5 100644 --- a/aggregator-server/internal/api/handlers/setup.go +++ b/aggregator-server/internal/api/handlers/setup.go @@ -1,13 +1,12 @@ package handlers import ( - "crypto/sha256" "database/sql" - "encoding/hex" "fmt" "net/http" "strconv" + "github.com/Fimeg/RedFlag/aggregator-server/internal/config" "github.com/gin-gonic/gin" "github.com/lib/pq" _ "github.com/lib/pq" @@ -374,8 +373,12 @@ func (h *SetupHandler) ConfigureServer(c *gin.Context) { return } - // Generate JWT secret for display (not logged for security) - jwtSecret := deriveJWTSecret(req.AdminUser, req.AdminPass) + // Generate secure JWT secret (not derived from credentials for security) + jwtSecret, err := config.GenerateSecureToken() + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate JWT secret"}) + return + } // Step 1: Update PostgreSQL password from bootstrap to user password fmt.Println("Updating PostgreSQL password from bootstrap to user-provided password...") @@ -398,7 +401,6 @@ func (h *SetupHandler) ConfigureServer(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "Configuration generated successfully!", - "jwtSecret": jwtSecret, "envContent": newEnvContent, "restartMessage": "Please replace the bootstrap environment variables with the newly generated ones, then run: docker-compose down && docker-compose up -d", "manualRestartRequired": true, @@ -407,8 +409,3 @@ func (h *SetupHandler) ConfigureServer(c *gin.Context) { }) } -// deriveJWTSecret generates a JWT secret from admin credentials -func deriveJWTSecret(username, password string) string { - hash := sha256.Sum256([]byte(username + password + "redflag-jwt-2024")) - return hex.EncodeToString(hash[:]) -} \ No newline at end of file diff --git a/aggregator-server/internal/config/config.go b/aggregator-server/internal/config/config.go index b9282dd..c9378ae 100644 --- a/aggregator-server/internal/config/config.go +++ b/aggregator-server/internal/config/config.go @@ -2,7 +2,6 @@ package config import ( "crypto/rand" - "crypto/sha256" "encoding/hex" "fmt" "os" @@ -123,12 +122,6 @@ func getEnv(key, defaultValue string) string { } -func deriveJWTSecret(username, password string) string { - // Derive JWT secret from admin credentials - // This ensures JWT secret changes if admin password changes - hash := sha256.Sum256([]byte(username + password + "redflag-jwt-2024")) - return hex.EncodeToString(hash[:]) -} // GenerateSecureToken generates a cryptographically secure random token func GenerateSecureToken() (string, error) { diff --git a/aggregator-web/src/components/SetupCompletionChecker.tsx b/aggregator-web/src/components/SetupCompletionChecker.tsx index da3565b..fcb2238 100644 --- a/aggregator-web/src/components/SetupCompletionChecker.tsx +++ b/aggregator-web/src/components/SetupCompletionChecker.tsx @@ -7,6 +7,7 @@ interface SetupCompletionCheckerProps { } export const SetupCompletionChecker: React.FC = ({ children }) => { + const [wasInSetupMode, setWasInSetupMode] = useState(false); const [isSetupMode, setIsSetupMode] = useState(null); const navigate = useNavigate(); const location = useLocation(); @@ -16,13 +17,28 @@ export const SetupCompletionChecker: React.FC = ({ try { const data = await setupApi.checkHealth(); - if (data.status === 'waiting for configuration') { - setIsSetupMode(true); - } else { - setIsSetupMode(false); + const currentSetupMode = data.status === 'waiting for configuration'; + + // Track if we were previously in setup mode + if (currentSetupMode) { + setWasInSetupMode(true); } + + // If we were in setup mode and now we're not, redirect to login + if (wasInSetupMode && !currentSetupMode && location.pathname === '/setup') { + console.log('Setup completed - redirecting to login'); + navigate('/login', { replace: true }); + return; // Prevent further state updates + } + + setIsSetupMode(currentSetupMode); } catch (error) { // If we can't reach the health endpoint, assume normal mode + if (wasInSetupMode && location.pathname === '/setup') { + console.log('Setup completed (endpoint reachable) - redirecting to login'); + navigate('/login', { replace: true }); + return; + } setIsSetupMode(false); } }; @@ -33,15 +49,7 @@ export const SetupCompletionChecker: React.FC = ({ const interval = setInterval(checkSetupStatus, 3000); return () => clearInterval(interval); - }, []); - - // If we're on the setup page and server is now healthy, redirect to login - useEffect(() => { - if (isSetupMode === false && location.pathname === '/setup') { - console.log('Setup completed - redirecting to login'); - navigate('/login', { replace: true }); - } - }, [isSetupMode, location.pathname, navigate]); + }, [wasInSetupMode, location.pathname, navigate]); // Always render children - this component only handles redirects return <>{children}; diff --git a/aggregator-web/src/pages/Setup.tsx b/aggregator-web/src/pages/Setup.tsx index 1266807..307c07a 100644 --- a/aggregator-web/src/pages/Setup.tsx +++ b/aggregator-web/src/pages/Setup.tsx @@ -21,8 +21,7 @@ const Setup: React.FC = () => { const navigate = useNavigate(); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); - const [jwtSecret, setJwtSecret] = useState(null); - const [envContent, setEnvContent] = useState(null); + const [envContent, setEnvContent] = useState(null); const [showSuccess, setShowSuccess] = useState(false); const [showPassword, setShowPassword] = useState(false); const [showDbPassword, setShowDbPassword] = useState(false); @@ -112,8 +111,7 @@ const Setup: React.FC = () => { try { const result = await setupApi.configure(formData); - // Store JWT secret, env content and show success screen - setJwtSecret(result.jwtSecret || null); + // Store env content and show success screen setEnvContent(result.envContent || null); setShowSuccess(true); toast.success(result.message || 'Configuration saved successfully!'); @@ -128,8 +126,8 @@ const Setup: React.FC = () => { } }; - // Success screen with credentials display - if (showSuccess && jwtSecret) { + // Success screen with configuration display + if (showSuccess && envContent) { return (
@@ -200,28 +198,7 @@ const Setup: React.FC = () => {
)} - {/* JWT Secret Section (Server Configuration) */} -
-

Server JWT Secret

-
- {jwtSecret} -
-
-

- For your information: This JWT secret is used internally by the server for session management and agent authentication. It's automatically included in the configuration file above. -

-
- -
- + {/* Next Steps */}

Next Steps