@page "/login" @using System.Diagnostics.CodeAnalysis @using Iceshrimp.Frontend.Components @using Iceshrimp.Frontend.Core.Miscellaneous @using Iceshrimp.Frontend.Core.Schemas @using Iceshrimp.Frontend.Core.Services @using Iceshrimp.Frontend.Localization @using Iceshrimp.Shared.Schemas.Web @using Microsoft.Extensions.Localization @using Iceshrimp.Assets.PhosphorIcons @inject ApiService Api @inject SessionService SessionService @inject NavigationManager Navigation @inject IStringLocalizer Loc; @inject MetadataService Metadata; @inject IJSRuntime Js; @inject ILogger Logger; @layout UnauthLayout

@Loc["Login to {0}", Name ?? "this Iceshrimp.NET Instance."]

@if (Loading) { Loading! } @if (Failure) { Authentication Failed } @if (SessionService.Users.Count > 0) {

@Loc["Existing sessions"]

@foreach(var user in SessionService.Users.Values) {
}
}

@Loc["Two-Factor Code required"]

@Loc["Processing"] @Loc["2FA Failed"] @Loc["Success!"] @Loc["Login"]
@code { [SupplyParameterFromQuery(Name = "rd")] [SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local")] private string? Redirect { get; set; } private string? Password { get; set; } private string? Username { get; set; } private bool Loading { get; set; } private bool Failure { get; set; } private string? Name { get; set; } private ElementReference Dialog { get; set; } private string? Otp { get; set; } private StateButton OtpButton { get; set; } = null!; private readonly Lazy> _moduleTask; public Login() { _moduleTask = new Lazy>(() => Js.InvokeAsync( "import", "./Pages/Settings/Account.razor.js") .AsTask()); } private async Task Submit() { var module = await _moduleTask.Value; Loading = true; try { if (Username == null || Password == null) { Loading = false; Failure = true; StateHasChanged(); // Manually triggering a state update, else component will not re-render. return; } var res = await Api.Auth.LoginAsync(new AuthRequest { Username = Username, Password = Password }); switch (res.Status) { case AuthStatusEnum.Authenticated: SessionService.AddUser(new StoredUser { // Token nor user will ever be null on an authenticated response Id = res.User!.Id, Username = res.User.Username, DisplayName = res.User.DisplayName, AvatarUrl = res.User.AvatarUrl, BannerUrl = res.User.BannerUrl, InstanceName = res.User.InstanceName, InstanceIconUrl = res.User.InstanceIconUrl, Token = res.Token!, Host = res.User.Host, IsAdmin = res.IsAdmin ?? false, Emojis = res.User.Emojis, MovedTo = res.User.MovedTo }); SessionService.SetSession(res.User.Id); Navigation.NavigateTo(Uri.TryCreate(Redirect, UriKind.Relative, out _) ? Redirect : "/", true); break; case AuthStatusEnum.Guest: Failure = true; Loading = false; break; case AuthStatusEnum.TwoFactor: await module.InvokeVoidAsync("openDialog", Dialog); Api.SetBearerToken(res.Token!); break; } } catch (ApiException) { Loading = false; Failure = false; StateHasChanged(); // Manually triggering a state update, else component will not re-render. } } private async Task TwoFactorLogin() { var module = await _moduleTask.Value; if (Otp is null) { return; } try { OtpButton.State = StateButton.StateEnum.Loading; StateHasChanged(); var res = await Api.Auth.SubmitTwoFactorAsync(new TwoFactorRequest { Code = Otp }); if (res.Status is AuthStatusEnum.Authenticated) { SessionService.AddUser(new StoredUser { Id = res.User!.Id, Username = res.User.Username, DisplayName = res.User.DisplayName, AvatarUrl = res.User.AvatarUrl, BannerUrl = res.User.BannerUrl, InstanceName = res.User.InstanceName, InstanceIconUrl = res.User.InstanceIconUrl, Token = res.Token!, Host = res.User.Host, IsAdmin = res.IsAdmin ?? false, Emojis = res.User.Emojis, MovedTo = res.User.MovedTo }); SessionService.SetSession(res.User.Id); Navigation.NavigateTo(Uri.TryCreate(Redirect, UriKind.Relative, out _) ? Redirect : "/", true); } else { Failure = true; Loading = false; OtpButton.State = StateButton.StateEnum.Failed; await module.InvokeVoidAsync("closeDialog", Dialog); OtpButton.State = StateButton.StateEnum.Initial; } } catch (ApiException e) { Logger.LogError(e, "2FA enrollment failed"); OtpButton.State = StateButton.StateEnum.Failed; } } private void LoginUser(StoredUser user) { SessionService.SetSession(user.Id); Navigation.NavigateTo(Uri.TryCreate(Redirect, UriKind.Relative, out _) ? Redirect : "/", true); } protected override async Task OnInitializedAsync() { var metadata = await Metadata.Instance.Value; Name = metadata.Name; } private async Task CloseDialog(ElementReference dialog) { var module = await _moduleTask.Value; await module.InvokeVoidAsync("closeDialog", dialog); } }