using System.Net.Http; using System.Text; using Jugenddienst_Stunden.Infrastructure; using Jugenddienst_Stunden.Interfaces; using Jugenddienst_Stunden.Models; using Jugenddienst_Stunden.Types; namespace Jugenddienst_Stunden.Services; internal sealed class AuthService : IAuthService { private readonly IApiClient _api; private readonly IAppSettings _settings; private readonly IAlertService _alerts; public AuthService(IApiClient api, IAppSettings settings, IAlertService alerts) { _api = api; _settings = settings; _alerts = alerts; } public async Task LoginWithCredentials(string username, string password, string serverUrl, CancellationToken ct = default) { if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password)) throw new Exception("Benutzername und Passwort werden benötigt."); var apiBase = NormalizeApiUrl(serverUrl); _settings.ApiUrl = apiBase; // BaseAddress für IApiClient setzen var content = new FormUrlEncodedContent(new[] { new KeyValuePair("user", username), new KeyValuePair("pass", password) }); // POST ohne Pfad – die API erwartet /appapi // Wichtig: Basis-URL hat garantiert einen abschließenden Slash (…/appapi/), // sodass ein leerer Pfad nicht zu Redirects führt (die den POST in GET verwandeln könnten). var res = await _api.SendAsync(HttpMethod.Post, string.Empty, content, null, ct).ConfigureAwait(false); if (res.user is null) throw new Exception(res.message ?? "Ungültige Antwort vom Server."); ApplyUser(res.user, apiBase); return res.user; } public async Task LoginWithToken(string token, CancellationToken ct = default) { if (string.IsNullOrWhiteSpace(token)) throw new Exception("Kein Token erkannt."); // QR-Token enthält die URL – extrahiere sie var td = new TokenData(token); // URL aus dem Token ebenfalls normalisieren, damit sie auf "/appapi/" endet _settings.ApiUrl = NormalizeApiUrl(td.Url); _settings.ApiKey = token; var res = await _api.GetAsync(string.Empty, null, ct).ConfigureAwait(false); if (res.user is null) throw new Exception(res.message ?? "Ungültige Antwort vom Server."); ApplyUser(res.user, td.Url); return res.user; } private void ApplyUser(User user, string apiBase) { _settings.ApiUrl = apiBase; // Wenn der Server keinen Token im User zurückliefert (QR-Login-Fall), bestehenden Token beibehalten var tokenToUse = string.IsNullOrWhiteSpace(user.Token) ? _settings.ApiKey : user.Token; _settings.ApiKey = tokenToUse; _settings.EmployeeId = user.Id; _settings.Name = user.Name; _settings.Surname = user.Surname; } private static string NormalizeApiUrl(string input) { if (string.IsNullOrWhiteSpace(input)) throw new Exception("Server-URL wird benötigt."); var url = input.Trim(); if (!url.StartsWith("http://", StringComparison.OrdinalIgnoreCase) && !url.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) { url = "https://" + url; } // Sicherstellen, dass der Pfad auf "/appapi" endet if (!url.EndsWith("/appapi", StringComparison.OrdinalIgnoreCase)) { url = url.TrimEnd('/') + "/appapi"; } // WICHTIG: Einen abschließenden Slash erzwingen, damit relative Pfade korrekt angehängt werden // und damit POST auf Basis-URL (leerem Pfad) nicht zu einem 301/302-Redirect führt, // der den Body (user/pass) verlieren könnte. //if (!url.EndsWith("/", StringComparison.Ordinal)) { // url += "/"; //} if (url.EndsWith("/", StringComparison.Ordinal)) { url = url.TrimEnd('/'); } return url; } }