using System.Net; using System.Net.Http.Headers; using System.Net.Http.Json; using Flaro.Api.Endpoints; namespace Flaro.Api.Tests; public class SignInTests { [Test] public async Task Mit_richtigem_Passwort_gibt_es_ein_Token() { using var server = new FlaroServer(); var anmeldung = await server.SetUpAndSignIn(); Assert.That(anmeldung.Token, Is.Not.Empty); Assert.That(anmeldung.User.UserName, Is.EqualTo("guido")); } [Test] public async Task Mit_falschem_Passwort_gibt_es_keines() { using var server = new FlaroServer(); await server.SetUpAndSignIn(); var antwort = await server.Client.PostAsJsonAsync("/api/v1/auth/login", new LoginRequest("guido", "falsch-aber-lang", "iPhone", "geraet-1")); Assert.That(antwort.StatusCode, Is.EqualTo(HttpStatusCode.Unauthorized)); } /// Unbekannter Name und falsches Passwort sind nicht zu unterscheiden. [Test] public async Task Ein_unbekannter_Name_antwortet_wie_ein_falsches_Passwort() { using var server = new FlaroServer(); await server.SetUpAndSignIn(); var unbekannt = await server.Client.PostAsJsonAsync("/api/v1/auth/login", new LoginRequest("gibtesnicht", "irgendein-passwort", "iPhone", "geraet-1")); var falsch = await server.Client.PostAsJsonAsync("/api/v1/auth/login", new LoginRequest("guido", "irgendein-passwort", "iPhone", "geraet-1")); Assert.That(unbekannt.StatusCode, Is.EqualTo(falsch.StatusCode)); // Nur die Begründung vergleichen, nicht den ganzen Körper: darin steht // je Anfrage eine neue Ablaufkennung, die nichts über das Konto sagt. static async Task Begründung(HttpResponseMessage antwort) => (await antwort.Content.ReadFromJsonAsync())! .RootElement.GetProperty("detail").GetString(); Assert.That(await Begründung(unbekannt), Is.EqualTo(await Begründung(falsch))); } [Test] public async Task Mit_Token_beantwortet_der_Server_wer_man_ist() { using var server = new FlaroServer(); var anmeldung = await server.SetUpAndSignIn(); var ich = await server.AlsAngemeldeter(anmeldung.Token) .GetFromJsonAsync("/api/v1/auth/me"); Assert.That(ich!.Id, Is.EqualTo(anmeldung.User.Id)); Assert.That(ich.IsAdministrator, Is.True); } [Test] public async Task Ohne_Token_antwortet_er_nicht() { using var server = new FlaroServer(); await server.SetUpAndSignIn(); // **Ohne Gedächtnis, weil der Client nach der Anmeldung ein Cookie // hält.** Sonst prüfte dieser Test nicht den Unangemeldeten, sondern // den Angemeldeten — und bestünde nie wieder. var antwort = await server.OhneGedaechtnis().GetAsync("/api/v1/auth/me"); Assert.That(antwort.StatusCode, Is.EqualTo(HttpStatusCode.Unauthorized)); } [Test] public async Task Ein_erfundenes_Token_gilt_nicht() { using var server = new FlaroServer(); await server.SetUpAndSignIn(); var antwort = await server.AlsAngemeldeter("ausgedacht-aber-lang-genug").GetAsync("/api/v1/auth/me"); Assert.That(antwort.StatusCode, Is.EqualTo(HttpStatusCode.Unauthorized)); } /// Nach dem Abmelden gilt das Token sofort nicht mehr. /// /// /// Der Grund, warum hier keine JWT stehen: ein signiertes Token gälte bis /// zum Ablauf weiter, und für ein verlorenes Telefon ist das der falsche /// Satz. /// [Test] public async Task Abmelden_entwertet_das_Token_augenblicklich() { using var server = new FlaroServer(); var anmeldung = await server.SetUpAndSignIn(); var client = server.AlsAngemeldeter(anmeldung.Token); (await client.PostAsync("/api/v1/auth/logout", null)).EnsureSuccessStatusCode(); var danach = await client.GetAsync("/api/v1/auth/me"); Assert.That(danach.StatusCode, Is.EqualTo(HttpStatusCode.Unauthorized)); } /// Dasselbe Gerät ersetzt seinen Eintrag, statt einen zweiten anzulegen. [Test] public async Task Eine_erneute_Anmeldung_desselben_Geraets_entwertet_das_alte_Token() { using var server = new FlaroServer(); var erste = await server.SetUpAndSignIn(); var zweite = await server.SignIn("guido", "ein-langes-passwort", "iPhone", "geraet-1"); Assert.That(zweite.Token, Is.Not.EqualTo(erste.Token)); var mitAltem = await server.AlsAngemeldeter(erste.Token).GetAsync("/api/v1/auth/me"); Assert.That(mitAltem.StatusCode, Is.EqualTo(HttpStatusCode.Unauthorized)); var mitNeuem = await server.AlsAngemeldeter(zweite.Token).GetAsync("/api/v1/auth/me"); Assert.That(mitNeuem.StatusCode, Is.EqualTo(HttpStatusCode.OK)); } /// Ein zweites Gerät meldet das erste nicht ab. [Test] public async Task Ein_anderes_Geraet_laesst_das_erste_in_Ruhe() { using var server = new FlaroServer(); var telefon = await server.SetUpAndSignIn(); var fernseher = await server.SignIn("guido", "ein-langes-passwort", "Apple TV", "geraet-2"); foreach (var token in new[] { telefon.Token, fernseher.Token }) { var antwort = await server.AlsAngemeldeter(token).GetAsync("/api/v1/auth/me"); Assert.That(antwort.StatusCode, Is.EqualTo(HttpStatusCode.OK)); } } }