From 3296ecdfb36f12007931a1b587859d6b164e49b1 Mon Sep 17 00:00:00 2001 From: Tim Krampitz Date: Sat, 8 Aug 2026 21:30:18 +0200 Subject: [PATCH] JWT- und Session-Validierung verbessert, UI aktualisiert MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - JWT-Authentifizierung prüft nun, ob User aktiv und existent ist - Session-Validierung beim App-Start via ValidateSessionAsync - Ungültige Sessions werden entfernt, SignalR-Verbindung getrennt - ApiService und IApiService um ValidateSessionAsync erweitert - LoginViewModel nutzt Session-Check und behandelt SignalR-Fehler - UI für eigene Phrasen auf mit Stil-Anpassung umgestellt --- SlipItIn.Server/Program.cs | 19 ++++++++++++++++ SlipItIn/App.xaml.cs | 18 +++++++++++---- SlipItIn/Services/ApiService.cs | 17 ++++++++++++++ SlipItIn/Services/Interfaces/IApiService.cs | 1 + SlipItIn/ViewModels/LoginViewModel.cs | 25 +++++++++++++++++---- SlipItIn/Views/GameBoardPage.xaml | 4 ++-- 6 files changed, 74 insertions(+), 10 deletions(-) diff --git a/SlipItIn.Server/Program.cs b/SlipItIn.Server/Program.cs index 70ded0c..55a9cc0 100644 --- a/SlipItIn.Server/Program.cs +++ b/SlipItIn.Server/Program.cs @@ -4,6 +4,7 @@ using Microsoft.IdentityModel.Tokens; using SlipItIn.Server.Data; using SlipItIn.Server.Hubs; using SlipItIn.Server.Services; +using System.Security.Claims; using System.Text; var builder = WebApplication.CreateBuilder(args); @@ -50,6 +51,24 @@ builder.Services context.Token = accessToken; } return Task.CompletedTask; + }, + OnTokenValidated = async context => + { + var userIdClaim = context.Principal?.FindFirst(ClaimTypes.NameIdentifier)?.Value; + if (!int.TryParse(userIdClaim, out var userId)) + { + context.Fail("Invalid user claim."); + return; + } + + var dbFactory = context.HttpContext.RequestServices.GetRequiredService>(); + await using var db = await dbFactory.CreateDbContextAsync(); + + var userExists = await db.Users.AnyAsync(u => u.Id == userId && u.IsActive); + if (!userExists) + { + context.Fail("User no longer exists or is inactive."); + } } }; }); diff --git a/SlipItIn/App.xaml.cs b/SlipItIn/App.xaml.cs index dcd0776..328a43d 100644 --- a/SlipItIn/App.xaml.cs +++ b/SlipItIn/App.xaml.cs @@ -35,16 +35,26 @@ public partial class App : Application var authSession = ServiceHelper.GetRequiredService(); var signalR = ServiceHelper.GetRequiredService(); var gameStateService = ServiceHelper.GetRequiredService(); + var apiService = ServiceHelper.GetRequiredService(); await authSession.InitializeAsync(); await gameStateService.InitializeAsync(); - if (!string.IsNullOrWhiteSpace(authSession.AccessToken)) + if (string.IsNullOrWhiteSpace(authSession.AccessToken)) + return; + + var isSessionValid = await apiService.ValidateSessionAsync(); + if (!isSessionValid) { - var connected = await signalR.ConnectAsync(authSession.AccessToken); - if (connected) - await gameStateService.ResyncAsync(); + await authSession.ClearSessionAsync(); + await gameStateService.ClearLocalGameDataAsync(); + await signalR.DisconnectAsync(); + return; } + + var connected = await signalR.ConnectAsync(authSession.AccessToken); + if (connected) + await gameStateService.ResyncAsync(); } catch { diff --git a/SlipItIn/Services/ApiService.cs b/SlipItIn/Services/ApiService.cs index b8970f7..d8273b3 100644 --- a/SlipItIn/Services/ApiService.cs +++ b/SlipItIn/Services/ApiService.cs @@ -28,6 +28,23 @@ public class ApiService : IApiService public Task LoginAsync(LoginRequestDto request, CancellationToken cancellationToken = default) => PostAuthAsync("api/auth/login", request, cancellationToken); + public async Task ValidateSessionAsync(CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(_authSession.AccessToken)) + return false; + + try + { + AttachAuthHeader(); + var response = await _httpClient.GetAsync("api/auth/me", cancellationToken); + return response.IsSuccessStatusCode; + } + catch + { + return false; + } + } + public Task LogoutAsync() => _authSession.ClearSessionAsync(); private async Task PostAuthAsync(string endpoint, TRequest request, CancellationToken cancellationToken) diff --git a/SlipItIn/Services/Interfaces/IApiService.cs b/SlipItIn/Services/Interfaces/IApiService.cs index f3fbe8a..d9ee135 100644 --- a/SlipItIn/Services/Interfaces/IApiService.cs +++ b/SlipItIn/Services/Interfaces/IApiService.cs @@ -6,5 +6,6 @@ public interface IApiService { Task RegisterAsync(RegisterRequestDto request, CancellationToken cancellationToken = default); Task LoginAsync(LoginRequestDto request, CancellationToken cancellationToken = default); + Task ValidateSessionAsync(CancellationToken cancellationToken = default); Task LogoutAsync(); } diff --git a/SlipItIn/ViewModels/LoginViewModel.cs b/SlipItIn/ViewModels/LoginViewModel.cs index ef8cbdb..887aecb 100644 --- a/SlipItIn/ViewModels/LoginViewModel.cs +++ b/SlipItIn/ViewModels/LoginViewModel.cs @@ -42,7 +42,11 @@ public partial class LoginViewModel : BaseViewModel }); await _authSession.SetSessionAsync(response); - await _signalR.ConnectAsync(response.Token); + + var connected = await _signalR.ConnectAsync(response.Token); + if (!connected) + throw new InvalidOperationException("Verbindung zum Spielserver fehlgeschlagen."); + await _gameStateService.InitializeAsync(); await Shell.Current.GoToAsync(nameof(LobbyPage)); }, "Anmeldung läuft..."); @@ -51,11 +55,24 @@ public partial class LoginViewModel : BaseViewModel [RelayCommand] public async Task InitializeAsync() { - bool isAuthenticated = _authSession.IsAuthenticated; + if (!_authSession.IsAuthenticated) + return; - if (isAuthenticated) + var isSessionValid = await _apiService.ValidateSessionAsync(); + if (!isSessionValid) { - await Shell.Current.GoToAsync(nameof(LobbyPage)); + await _authSession.ClearSessionAsync(); + await _gameStateService.ClearLocalGameDataAsync(); + await _signalR.DisconnectAsync(); + return; } + + var token = _authSession.AccessToken; + if (string.IsNullOrWhiteSpace(token)) + return; + + var connected = await _signalR.ConnectAsync(token); + if (connected) + await Shell.Current.GoToAsync(nameof(LobbyPage)); } } diff --git a/SlipItIn/Views/GameBoardPage.xaml b/SlipItIn/Views/GameBoardPage.xaml index c27f2bd..783deff 100644 --- a/SlipItIn/Views/GameBoardPage.xaml +++ b/SlipItIn/Views/GameBoardPage.xaml @@ -20,7 +20,7 @@ - +