authenticate performs the authentication flow with Proxmox API using username/password. This method handles the complete authentication process: - Sends POST request to /access/ticket endpoint - Validates the response and extracts authentication data - Creates and caches the AuthToken with proper e
(ctx context.Context)
| 269 | // |
| 270 | // Returns the new authentication token or an error if authentication fails. |
| 271 | func (am *AuthManager) authenticate(ctx context.Context) (*AuthToken, error) { |
| 272 | am.mu.Lock() |
| 273 | defer am.mu.Unlock() |
| 274 | |
| 275 | // Double-check after acquiring write lock |
| 276 | if am.authToken != nil && am.authToken.IsValid() { |
| 277 | return am.authToken, nil |
| 278 | } |
| 279 | |
| 280 | am.logger.Debug("Authenticating with Proxmox API: %s", am.username) |
| 281 | |
| 282 | // Prepare authentication request |
| 283 | authURL := EndpointAccessTicket |
| 284 | am.logger.Debug("Authentication URL: %s", authURL) |
| 285 | |
| 286 | // Create form data |
| 287 | formData := url.Values{} |
| 288 | formData.Set("username", am.username) |
| 289 | formData.Set("password", am.password) |
| 290 | am.logger.Debug("Sending authentication request for user: %s", am.username) |
| 291 | |
| 292 | // Create HTTP request |
| 293 | req, err := http.NewRequestWithContext(ctx, http.MethodPost, am.httpClient.baseURL+authURL, strings.NewReader(formData.Encode())) |
| 294 | if err != nil { |
| 295 | return nil, fmt.Errorf("failed to create authentication request: %w", err) |
| 296 | } |
| 297 | |
| 298 | // Set headers |
| 299 | req.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| 300 | req.Header.Set("User-Agent", "pvetui") |
| 301 | |
| 302 | am.logger.Debug("Sending authentication request to: %s", am.httpClient.baseURL+authURL) |
| 303 | |
| 304 | // Execute request |
| 305 | resp, err := am.httpClient.client.Do(req) |
| 306 | if err != nil { |
| 307 | return nil, fmt.Errorf("authentication request failed: %w", err) |
| 308 | } |
| 309 | defer func() { |
| 310 | _ = resp.Body.Close() |
| 311 | }() |
| 312 | |
| 313 | am.logger.Debug("Authentication response status: %d %s", resp.StatusCode, resp.Status) |
| 314 | |
| 315 | // Check response status |
| 316 | if resp.StatusCode != http.StatusOK { |
| 317 | // Read response body for better error details |
| 318 | body, _ := io.ReadAll(resp.Body) |
| 319 | am.logger.Debug("Authentication failed response body: %s", string(body)) |
| 320 | |
| 321 | return nil, fmt.Errorf("authentication failed with status %d: %s", resp.StatusCode, resp.Status) |
| 322 | } |
| 323 | |
| 324 | // Parse response |
| 325 | var authResponse struct { |
| 326 | Data struct { |
| 327 | Ticket string `json:"ticket"` |
| 328 | CSRFPreventionToken string `json:"CSRFPreventionToken"` |