95 lines
3.9 KiB
C#
95 lines
3.9 KiB
C#
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<User> 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<string, string>("user", username),
|
||
new KeyValuePair<string, string>("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<BaseResponse>(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<User> 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<BaseResponse>(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;
|
||
}
|
||
}
|