Files
Shogi/Shogi.UI/Pages/Home/Account/AccountManager.cs
2023-07-07 16:46:26 -05:00

138 lines
5.0 KiB
C#

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;
}
}
/// <summary>
/// Try to log in with the account used from the previous browser session.
/// </summary>
/// <returns>True if login succeeded.</returns>
public async Task<bool> 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.");
}
}
}