using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components.WebAssembly.Authentication; using Shogi.UI.Pages.Home.Api; using Shogi.UI.Shared; namespace Shogi.UI.Pages.Home.Account; public class AccountManager { private readonly AccountState accountState; private readonly IShogiApi shogiApi; private readonly ILocalStorage localStorage; private readonly AuthenticationStateProvider authState; private readonly NavigationManager navigation; private readonly ShogiSocket shogiSocket; public AccountManager( AccountState accountState, IShogiApi unauthenticatedClient, AuthenticationStateProvider authState, ILocalStorage localStorage, NavigationManager navigation, ShogiSocket shogiSocket) { this.accountState = accountState; this.shogiApi = unauthenticatedClient; this.authState = authState; this.localStorage = localStorage; this.navigation = navigation; this.shogiSocket = shogiSocket; } private Task SetUser(User user) => accountState.SetUser(user); public async Task LoginWithGuestAccount() { var response = await shogiApi.GetToken(WhichAccountPlatform.Guest); if (response != null) { await SetUser(new User { DisplayName = response.DisplayName, Id = response.UserId, WhichAccountPlatform = WhichAccountPlatform.Guest }); await localStorage.SetAccountPlatform(WhichAccountPlatform.Guest); // TODO: OpenAsync() sometimes doesn't return, probably because of the fire'n'forget task inside it. Figure that out. await shogiSocket.OpenAsync(response.OneTimeToken.ToString()); } else { throw new InvalidOperationException("Failed to get token from server during guest login."); } } public async Task LoginWithMicrosoftAccount() { var state = await authState.GetAuthenticationStateAsync(); if (state?.User?.Identity?.Name == null || state.User?.Identity?.IsAuthenticated == false) { // Set the login platform so that we know to log in with microsoft after being redirected away from the UI. await localStorage.SetAccountPlatform(WhichAccountPlatform.Microsoft); navigation.NavigateToLogin("authentication/login"); return; } } /// /// Try to log in with the account used from the previous browser session. /// /// True if login succeeded. public async Task TryLoginSilentAsync() { var platform = await localStorage.GetAccountPlatform(); Console.WriteLine($"Try Login Silent - {platform}"); if (platform == WhichAccountPlatform.Guest) { var response = await shogiApi.GetToken(WhichAccountPlatform.Guest); if (response != null) { await accountState.SetUser(new User( Id: response.UserId, DisplayName: response.DisplayName, WhichAccountPlatform: WhichAccountPlatform.Guest)); await shogiSocket.OpenAsync(response.OneTimeToken.ToString()); return true; } } else if (platform == WhichAccountPlatform.Microsoft) { var state = await authState.GetAuthenticationStateAsync(); if (state.User?.Identity?.Name != null) { var response = await shogiApi.GetToken(WhichAccountPlatform.Microsoft); if (response == null) { // Login failed, so reset local storage to avoid putting the user in a broken state. await localStorage.DeleteAccountPlatform(); return false; } var id = state.User.Claims.Single(claim => claim.Type == "oid").Value; var displayName = state.User.Identity.Name; await accountState.SetUser(new User( Id: id, DisplayName: displayName, WhichAccountPlatform: WhichAccountPlatform.Microsoft)); await shogiSocket.OpenAsync(response.OneTimeToken.ToString()); return true; } } return false; } public async Task LogoutAsync() { var platform = await localStorage.GetAccountPlatform(); await localStorage.DeleteAccountPlatform(); await accountState.SetUser(null); if (platform == WhichAccountPlatform.Guest) { await shogiApi.GuestLogout(); } else if (platform == WhichAccountPlatform.Microsoft) { navigation.NavigateToLogout("authentication/logout"); } else { throw new InvalidOperationException("Tried to logout without a valid account platform."); } } }