using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using LANCommander.Launcher.Models; using LANCommander.SDK; using Microsoft.Extensions.Logging; using Microsoft.IdentityModel.Tokens; namespace LANCommander.Launcher.Services; public class AuthenticationService : BaseService { private Settings Settings; private bool TemporarilyOffline; public event EventHandler OnLogin; public event EventHandler OnLogout; public event EventHandler OnRegister; public delegate void OnOfflineModeChangedHandler(bool state); public event OnOfflineModeChangedHandler OnOfflineModeChanged; public AuthenticationService( Client client, ILogger logger) : base(client, logger) { Settings = SettingService.GetSettings(); } public bool IsConnected() { return Client.IsConnected(); } public string GetServerAddress() { return Client.IsConfigured() ? Client.GetServerAddress() : string.Empty; } public async Task IsServerOnlineAsync() { try { if (Client.IsConfigured()) { return await Client.PingAsync(); } } catch { } return false; } public async Task Login() { await Login(Settings.Authentication.ServerAddress, new SDK.Models.AuthToken { AccessToken = Settings.Authentication.AccessToken, RefreshToken = Settings.Authentication.RefreshToken, }); } public async Task Login(string serverAddress, string username, string password) { await Client.ChangeServerAddressAsync(serverAddress); var token = await Client.AuthenticateAsync(username, password); await Login(serverAddress, token); } public async Task Login(string serverAddress, SDK.Models.AuthToken token) { try { await Client.ChangeServerAddressAsync(serverAddress); Settings = SettingService.GetSettings(); Settings.Authentication.ServerAddress = serverAddress; Settings.Authentication.AccessToken = token.AccessToken; Settings.Authentication.RefreshToken = token.RefreshToken; Client.UseToken(token); if (await Client.ValidateTokenAsync()) { SetOfflineMode(false); TemporarilyOffline = false; SettingService.SaveSettings(Settings); var user = await Client.Profile.GetAsync(); OnLogin?.Invoke(this, EventArgs.Empty); } } catch { } } public async Task Register(string serverAddress, string username, string password, string passwordConfirmation) { if (String.IsNullOrWhiteSpace(serverAddress)) throw new Exception("Server address cannot be blank"); if (String.IsNullOrWhiteSpace(username)) throw new Exception("Username cannot be blank"); if (String.IsNullOrWhiteSpace(password)) throw new Exception("Password cannot be blank"); if (password != passwordConfirmation) throw new Exception("Passwords do not match"); await Client.ChangeServerAddressAsync(serverAddress); var token = await Client.RegisterAsync(username, password, passwordConfirmation); Client.UseToken(token); Settings = SettingService.GetSettings(); Settings.Authentication.ServerAddress = serverAddress; Settings.Authentication.AccessToken = token.AccessToken; Settings.Authentication.RefreshToken = token.RefreshToken; SettingService.SaveSettings(Settings); OnRegister?.Invoke(this, EventArgs.Empty); } public async Task ValidateConnectionAsync() { return await Client.ValidateTokenAsync(); } public bool OfflineModeEnabled() { return TemporarilyOffline || Settings.Authentication.OfflineMode; } public void LoginOffline() { TemporarilyOffline = true; OnOfflineModeChanged?.Invoke(true); } public void SetOfflineMode(bool state) { Settings = SettingService.GetSettings(); Settings.Authentication.OfflineMode = state; if (state) Client.Disconnect(); SettingService.SaveSettings(Settings); OnOfflineModeChanged?.Invoke(state); } public async Task Logout() { await Client.LogoutAsync(); TemporarilyOffline = false; Settings = SettingService.GetSettings(); Settings.Authentication = new AuthenticationSettings { ServerAddress = Settings.Authentication.ServerAddress, // keep server address when logging out }; SettingService.SaveSettings(Settings); OnLogout?.Invoke(this, EventArgs.Empty); } public Guid GetUserId() { var decodedToken = DecodeToken(); if (decodedToken == null) return Guid.Empty; var claim = decodedToken.Claims.First(c => c.Type == ClaimTypes.NameIdentifier); if (Guid.TryParse(claim.Value, out Guid id)) return id; return Guid.Empty; } public string GetCurrentUserName() { var decodedToken = DecodeToken(); if (decodedToken == null) return String.Empty; return decodedToken.Claims?.FirstOrDefault(claim => claim.Type == ClaimTypes.Name)?.Value ?? string.Empty; } public JwtSecurityToken? DecodeToken() { Settings = SettingService.GetSettings(); if (string.IsNullOrEmpty(Settings.Authentication.AccessToken)) return null; try { var handler = new JwtSecurityTokenHandler(); return handler.ReadToken(Settings.Authentication.AccessToken) as JwtSecurityToken; } catch { return null; } } public bool HasStoredCredentials() { if (string.IsNullOrEmpty(Settings.Authentication.AccessToken)) return false; var decodedToken = DecodeToken(); return decodedToken != null; } public async Task OfflineModeAvailableAsync() { return !(await IsServerOnlineAsync()) && !IsConnected() && HasStoredCredentials(); } }