|
18 | 18 | using System.IO; |
19 | 19 | using System.Text; |
20 | 20 | using System.Net.Sockets; |
| 21 | +using System.Text; |
21 | 22 | using System.Text.Json; |
22 | 23 | using System.Security.Cryptography; |
23 | 24 | using System.Threading; |
@@ -127,104 +128,35 @@ private async Task<bool> AuthenticateAsync() |
127 | 128 |
|
128 | 129 | try |
129 | 130 | { |
130 | | - // Read greeting and challenge from gateway |
131 | | - var responseBuffer = new byte[1024]; |
132 | | - var messageBuffer = new StringBuilder(); |
133 | | - |
134 | | - // Read until we have both the version and cram messages |
135 | | - string? version = null; |
136 | | - string? cram = null; |
137 | | - |
138 | | - while (version == null || cram == null) |
| 131 | + // DataBento authentication message format |
| 132 | + var authMessage = new |
139 | 133 | { |
140 | | - var authenticationBytesRead = await _stream.ReadAsync(responseBuffer, 0, responseBuffer.Length); |
141 | | - if (authenticationBytesRead == 0) |
142 | | - { |
143 | | - Log.Error("DatabentoRawClient.AuthenticateAsync(): Connection closed during authentication"); |
144 | | - return false; |
145 | | - } |
146 | | - |
147 | | - var receivedData = Encoding.UTF8.GetString(responseBuffer, 0, authenticationBytesRead); |
148 | | - messageBuffer.Append(receivedData); |
149 | | - |
150 | | - var messages = messageBuffer.ToString().Split('\n', StringSplitOptions.RemoveEmptyEntries); |
151 | | - |
152 | | - foreach (var msg in messages) |
153 | | - { |
154 | | - if (msg.StartsWith("lsg_version=")) |
155 | | - { |
156 | | - version = msg.Substring("lsg_version=".Length); |
157 | | - Log.Trace($"DatabentoRawClient.AuthenticateAsync(): Gateway version: {version}"); |
158 | | - } |
159 | | - else if (msg.StartsWith("cram=")) |
160 | | - { |
161 | | - cram = msg.Substring("cram=".Length); |
162 | | - Log.Trace($"DatabentoRawClient.AuthenticateAsync(): Received CRAM challenge"); |
163 | | - } |
164 | | - } |
165 | | - } |
| 134 | + msg_type = "authenticate", |
| 135 | + api_key = _apiKey, |
| 136 | + version = "1.0" |
| 137 | + }; |
| 138 | + |
| 139 | + var authJson = JsonSerializer.Serialize(authMessage); |
| 140 | + var authBytes = Encoding.UTF8.GetBytes(authJson + "\n"); |
166 | 141 |
|
167 | | - if (string.IsNullOrEmpty(cram)) |
168 | | - { |
169 | | - Log.Error("DatabentoRawClient.AuthenticateAsync(): No CRAM challenge received"); |
170 | | - return false; |
171 | | - } |
172 | | - |
173 | | - // Generate authentication response |
174 | | - // Concatenate cram and API key: $cram|$key |
175 | | - var cramAndKey = $"{cram}|{_apiKey}"; |
176 | | - |
177 | | - // Hash with SHA-256 |
178 | | - string authHash; |
179 | | - using (var sha256 = SHA256.Create()) |
180 | | - { |
181 | | - var hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(cramAndKey)); |
182 | | - authHash = BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant(); |
183 | | - } |
184 | | - |
185 | | - // Get last 5 characters of API key (bucket_id) |
186 | | - var bucketId = _apiKey.Substring(_apiKey.Length - 5); |
187 | | - |
188 | | - // Create auth response: $hash-$bucket_id |
189 | | - var authResponse = $"{authHash}-{bucketId}"; |
190 | | - |
191 | | - // Send authentication message |
192 | | - // Format: auth=$authResponse|dataset=GLBX.MDP3|encoding=dbn|ts_out=0\n |
193 | | - var authMessage = $"auth={authResponse}|dataset=GLBX.MDP3|encoding=json|ts_out=0\n"; |
194 | | - var authBytes = Encoding.UTF8.GetBytes(authMessage); |
195 | | - |
196 | 142 | await _stream.WriteAsync(authBytes, 0, authBytes.Length); |
197 | 143 | await _stream.FlushAsync(); |
198 | | - |
199 | | - Log.Trace("DatabentoRawClient.AuthenticateAsync(): Sent authentication message"); |
200 | | - |
| 144 | + |
201 | 145 | // Read authentication response |
202 | 146 | messageBuffer.Clear(); |
203 | 147 | var bytesRead = await _stream.ReadAsync(responseBuffer, 0, responseBuffer.Length); |
204 | 148 | var response = Encoding.UTF8.GetString(responseBuffer, 0, bytesRead); |
205 | | - |
206 | | - Log.Trace($"DatabentoRawClient.AuthenticateAsync(): Auth response: {response}"); |
207 | | - |
208 | | - // Check for success response: success=1|session_id=... |
209 | | - if (response.Contains("success=1")) |
| 149 | + |
| 150 | + Log.Debug($"DatabentoRawClient.AuthenticateAsync(): Auth response: {response}"); |
| 151 | + |
| 152 | + // Parse response to check if authentication was successful |
| 153 | + var responseDoc = JsonDocument.Parse(response.Trim()); |
| 154 | + if (responseDoc.RootElement.TryGetProperty("success", out var successElement)) |
210 | 155 | { |
211 | | - // Extract session ID if needed |
212 | | - if (response.Contains("session_id=")) |
213 | | - { |
214 | | - var sessionIdStart = response.IndexOf("session_id=") + "session_id=".Length; |
215 | | - var sessionIdEnd = response.IndexOf('|', sessionIdStart); |
216 | | - if (sessionIdEnd == -1) sessionIdEnd = response.IndexOf('\n', sessionIdStart); |
217 | | - if (sessionIdEnd > sessionIdStart) |
218 | | - { |
219 | | - var sessionId = response.Substring(sessionIdStart, sessionIdEnd - sessionIdStart); |
220 | | - Log.Trace($"DatabentoRawClient.AuthenticateAsync(): Session ID: {sessionId}"); |
221 | | - } |
222 | | - } |
223 | | - return true; |
| 156 | + return successElement.GetBoolean(); |
224 | 157 | } |
225 | | - |
226 | | - Log.Trace($"DatabentoRawClient.AuthenticateAsync(): Authentication failed: {response}"); |
227 | | - return false; |
| 158 | + |
| 159 | + return response.Contains("success") || response.Contains("authenticated"); |
228 | 160 | } |
229 | 161 | catch (Exception ex) |
230 | 162 | { |
|
0 commit comments