From fc2245c1cf3a88ffad1aee8d577bc6016b5175b0 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 28 Dec 2025 05:24:32 +0000 Subject: [PATCH] fix: Add login functionality to editor upload dialog Previously, when users tried to upload from the editor to the server, they would see a "not authenticated" error even after logging into the editor. This was because there are two separate authentication systems: 1. Packet-based authentication for editor-to-game-server connection 2. HTTP token authentication for web API endpoints (updates/uploads) The upload functionality requires HTTP token authentication, but users who logged in normally through the editor only had packet-based auth. Changes made: - Added a "Login" button to the upload form that appears when not authenticated - Implemented btnLogin_Click handler that prompts for credentials - Added authentication via IntersectHttpClient.TryRequestToken() - Token is saved to preferences for future sessions - Credentials are hashed and saved for convenience - Updated authentication status display to guide users to click Login This allows users to authenticate directly from the upload dialog without needing to go through the editor update flow first. Fixes authentication issues when uploading from editor to server. --- .../Forms/FrmUploadToServer.Designer.cs | 14 ++ Intersect.Editor/Forms/FrmUploadToServer.cs | 159 +++++++++++++++++- 2 files changed, 172 insertions(+), 1 deletion(-) diff --git a/Intersect.Editor/Forms/FrmUploadToServer.Designer.cs b/Intersect.Editor/Forms/FrmUploadToServer.Designer.cs index 0cc8378cc7..120935b4e4 100644 --- a/Intersect.Editor/Forms/FrmUploadToServer.Designer.cs +++ b/Intersect.Editor/Forms/FrmUploadToServer.Designer.cs @@ -44,6 +44,7 @@ private void InitializeComponent() this.progressBar = new System.Windows.Forms.ProgressBar(); this.lblStatus = new DarkUI.Controls.DarkLabel(); this.grpUploadType = new DarkUI.Controls.DarkGroupBox(); + this.btnLogin = new DarkUI.Controls.DarkButton(); this.grpUploadType.SuspendLayout(); this.SuspendLayout(); // @@ -158,6 +159,17 @@ private void InitializeComponent() this.lblStatus.Size = new System.Drawing.Size(0, 13); this.lblStatus.TabIndex = 8; // + // btnLogin + // + this.btnLogin.Location = new System.Drawing.Point(15, 260); + this.btnLogin.Name = "btnLogin"; + this.btnLogin.Padding = new System.Windows.Forms.Padding(5); + this.btnLogin.Size = new System.Drawing.Size(85, 28); + this.btnLogin.TabIndex = 11; + this.btnLogin.Text = "Login"; + this.btnLogin.Visible = false; + this.btnLogin.Click += new System.EventHandler(this.btnLogin_Click); + // // btnUpload // this.btnUpload.Enabled = false; @@ -185,6 +197,7 @@ private void InitializeComponent() this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(484, 300); + this.Controls.Add(this.btnLogin); this.Controls.Add(this.btnClose); this.Controls.Add(this.btnUpload); this.Controls.Add(this.lblStatus); @@ -223,5 +236,6 @@ private void InitializeComponent() private System.Windows.Forms.ProgressBar progressBar; private DarkLabel lblStatus; private DarkGroupBox grpUploadType; + private DarkButton btnLogin; } } diff --git a/Intersect.Editor/Forms/FrmUploadToServer.cs b/Intersect.Editor/Forms/FrmUploadToServer.cs index ee76f59cb3..bb7496dfe7 100644 --- a/Intersect.Editor/Forms/FrmUploadToServer.cs +++ b/Intersect.Editor/Forms/FrmUploadToServer.cs @@ -3,6 +3,8 @@ using System.Linq; using System.Net.Http; using System.Net.Http.Headers; +using System.Security.Cryptography; +using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using DarkUI.Forms; @@ -80,10 +82,165 @@ private void UpdateAuthenticationStatus() if (_tokenResponse != null) { lblStatus.Text = "✓ Authenticated"; + btnLogin.Visible = false; } else { - lblStatus.Text = "⚠ Not authenticated - login through the editor first"; + lblStatus.Text = "⚠ Not authenticated - click Login to authenticate"; + btnLogin.Visible = true; + } + } + + private async void btnLogin_Click(object sender, EventArgs e) + { + // Check if server URL is set + if (string.IsNullOrWhiteSpace(txtServerUrl.Text)) + { + DarkMessageBox.ShowError( + "Please enter a server URL before logging in.", + "Server URL Required", + DarkDialogButton.Ok, + Icon + ); + return; + } + + // Prompt for credentials + using var loginDialog = new Form + { + Text = "Login", + Width = 350, + Height = 180, + FormBorderStyle = FormBorderStyle.FixedDialog, + StartPosition = FormStartPosition.CenterParent, + MaximizeBox = false, + MinimizeBox = false + }; + + var lblUsername = new Label { Left = 20, Top = 20, Text = "Username:", Width = 80 }; + var txtUsername = new TextBox { Left = 110, Top = 17, Width = 200 }; + var lblPassword = new Label { Left = 20, Top = 50, Text = "Password:", Width = 80 }; + var txtPassword = new TextBox { Left = 110, Top = 47, Width = 200, PasswordChar = '*' }; + var btnOk = new Button { Text = "Login", Left = 150, Top = 85, DialogResult = DialogResult.OK }; + var btnCancel = new Button { Text = "Cancel", Left = 230, Top = 85, DialogResult = DialogResult.Cancel }; + + loginDialog.Controls.AddRange(new Control[] { lblUsername, txtUsername, lblPassword, txtPassword, btnOk, btnCancel }); + loginDialog.AcceptButton = btnOk; + loginDialog.CancelButton = btnCancel; + + // Load saved credentials if available + var savedUsername = Preferences.LoadPreference("Username"); + var savedPassword = Preferences.LoadPreference("Password"); + if (!string.IsNullOrWhiteSpace(savedUsername)) + { + txtUsername.Text = savedUsername; + if (!string.IsNullOrWhiteSpace(savedPassword)) + { + txtPassword.Text = "*****"; + txtPassword.Tag = savedPassword; // Store hashed password in Tag + } + } + + if (loginDialog.ShowDialog() == DialogResult.OK) + { + var username = txtUsername.Text.Trim(); + var password = txtPassword.Text.Trim(); + + if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password)) + { + DarkMessageBox.ShowError( + "Username and password are required.", + "Invalid Credentials", + DarkDialogButton.Ok, + Icon + ); + return; + } + + // Disable controls during authentication + btnLogin.Enabled = false; + lblStatus.Text = "Authenticating..."; + + try + { + var serverUrl = txtServerUrl.Text.TrimEnd('/'); + using var httpClient = new IntersectHttpClient(serverUrl); + + // Check if we're using a saved password (hashed) + var isHashedPassword = password == "*****" && txtPassword.Tag is string hashedPwd; + var passwordToUse = isHashedPassword ? (string)txtPassword.Tag! : password; + + var tokenResult = httpClient.TryRequestToken( + username, + passwordToUse, + out var tokenResponse, + hashed: isHashedPassword + ); + + if (tokenResult == TokenResultType.TokenReceived && tokenResponse != null) + { + _tokenResponse = tokenResponse; + + // Save token to preferences + Preferences.SavePreference(nameof(TokenResponse), JsonConvert.SerializeObject(_tokenResponse)); + + // Save credentials for future use + Preferences.SavePreference("Username", username); + if (!isHashedPassword) + { + // Hash the password before saving + using var sha = SHA256.Create(); + var hashedPassword = BitConverter.ToString( + sha.ComputeHash(Encoding.UTF8.GetBytes(password)) + ).Replace("-", ""); + Preferences.SavePreference("Password", hashedPassword); + } + + UpdateAuthenticationStatus(); + + DarkMessageBox.ShowInformation( + "Successfully authenticated!", + "Login Successful", + DarkDialogButton.Ok, + Icon + ); + } + else + { + var errorMessage = tokenResult switch + { + TokenResultType.InvalidCredentials => "Invalid username or password.", + TokenResultType.InvalidUsername => "Invalid username.", + TokenResultType.InvalidPassword => "Invalid password.", + TokenResultType.RequestError => "Network error. Please check your server URL.", + _ => $"Authentication failed: {tokenResult}" + }; + + DarkMessageBox.ShowError( + errorMessage, + "Login Failed", + DarkDialogButton.Ok, + Icon + ); + } + } + catch (Exception ex) + { + DarkMessageBox.ShowError( + $"Authentication error: {ex.Message}", + "Login Error", + DarkDialogButton.Ok, + Icon + ); + } + finally + { + btnLogin.Enabled = true; + if (_tokenResponse == null) + { + UpdateAuthenticationStatus(); + } + } } }