Merge pull request #24 from Moonlight-Panel/TOTP

Totp
This commit is contained in:
Daniel Balk
2023-04-03 04:09:09 +02:00
committed by GitHub
5 changed files with 89 additions and 44 deletions

View File

@@ -1,6 +1,6 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
namespace Moonlight.App.Models.Misc; namespace Moonlight.App.Models.Forms;
public class LoginDataModel public class LoginDataModel
{ {

View File

@@ -0,0 +1,9 @@
using System.ComponentModel.DataAnnotations;
namespace Moonlight.App.Models.Forms;
public class LoginTotpDataModel
{
[Required(ErrorMessage = "You need to enter a 2fa code")]
public string Code { get; set; } = "";
}

View File

@@ -13,6 +13,8 @@
@using Moonlight.App.Models.Misc @using Moonlight.App.Models.Misc
@using Moonlight.App.Services.OAuth2 @using Moonlight.App.Services.OAuth2
@using Moonlight.App.Services.Sessions @using Moonlight.App.Services.Sessions
@using System.ComponentModel.DataAnnotations
@using Moonlight.App.Models.Forms
@inject AlertService AlertService @inject AlertService AlertService
@inject UserService UserService @inject UserService UserService
@@ -26,9 +28,9 @@
<div class="card rounded-3 w-md-550px"> <div class="card rounded-3 w-md-550px">
<div class="card-body"> <div class="card-body">
<div class="d-flex flex-center flex-column-fluid pb-15 pb-lg-20"> <div class="d-flex flex-center flex-column-fluid pb-15 pb-lg-20">
<SmartForm Model="User" OnValidSubmit="DoLogin"> @if (!TotpRequired)
@if (!TotpRequired) {
{ <SmartForm Model="LoginData" OnValidSubmit="DoLogin">
<div class="text-center mt-3 mb-11"> <div class="text-center mt-3 mb-11">
<h1 class="text-dark fw-bolder mb-3"> <h1 class="text-dark fw-bolder mb-3">
<TL>Sign In</TL> <TL>Sign In</TL>
@@ -64,11 +66,11 @@
</div> </div>
<div class="mt-3 mb-3"> <div class="mt-3 mb-3">
<InputText @bind-Value="User.Email" type="email" placeholder="@(SmartTranslateService.Translate("Email"))" class="form-control bg-transparent"/> <InputText @bind-Value="LoginData.Email" type="email" placeholder="@(SmartTranslateService.Translate("Email"))" class="form-control bg-transparent"/>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<InputText @bind-Value="User.Password" type="password" placeholder="@(SmartTranslateService.Translate("Password"))" class="form-control bg-transparent"/> <InputText @bind-Value="LoginData.Password" type="password" placeholder="@(SmartTranslateService.Translate("Password"))" class="form-control bg-transparent"/>
</div> </div>
<div class="d-flex flex-stack flex-wrap gap-3 fs-base fw-semibold mb-8"> <div class="d-flex flex-stack flex-wrap gap-3 fs-base fw-semibold mb-8">
@@ -84,29 +86,29 @@
<TL>Sign-in</TL> <TL>Sign-in</TL>
</button> </button>
</div> </div>
}
else <div class="text-gray-500 text-center fw-semibold fs-6">
{ <TL>Not registered yet?</TL>
<a href="/register" class="link-primary">
<TL>Sign up</TL>
</a>
</div>
</SmartForm>
}
else
{
<SmartForm Model="TotpData" OnValidSubmit="DoLogin">
<div class="fv-row mb-8 fv-plugins-icon-container"> <div class="fv-row mb-8 fv-plugins-icon-container">
<input type="number" class="form-control bg-transparent"> <InputText @bind-Value="TotpData.Code" type="number" class="form-control bg-transparent"></InputText>
</div> </div>
<div class="d-grid mb-10"> <div class="d-grid mb-10">
<WButton Text="@(SmartTranslateService.Translate("Sign-in"))" <button type="submit" class="btn btn-primary">
WorkingText="@(SmartTranslateService.Translate("Working"))" <TL>Sign-in</TL>
CssClasses="btn-primary" </button>
OnClick="DoLogin">
</WButton>
</div> </div>
} </SmartForm>
}
<div class="text-gray-500 text-center fw-semibold fs-6">
<TL>Not registered yet?</TL>
<a href="/register" class="link-primary">
<TL>Sign up</TL>
</a>
</div>
</SmartForm>
</div> </div>
</div> </div>
</div> </div>
@@ -114,22 +116,39 @@
@code @code
{ {
private LoginDataModel User = new(); private LoginDataModel LoginData = new();
private LoginTotpDataModel TotpData = new();
private bool TotpRequired = false; private bool TotpRequired = false;
private string TotpCode = "";
private async Task DoLogin() private async Task DoLogin()
{ {
try try
{ {
User.Email = User.Email.ToLower().Trim(); LoginData.Email = LoginData.Email.ToLower().Trim();
TotpRequired = await UserService.CheckTotp(User.Email, User.Password); if (string.IsNullOrEmpty(TotpData.Code))
if (!TotpRequired)
{ {
var token = await UserService.Login(User.Email, User.Password); TotpRequired = await UserService.CheckTotp(LoginData.Email, LoginData.Password);
if (!TotpRequired)
{
var token = await UserService.Login(LoginData.Email, LoginData.Password);
await CookieService.SetValue("token", token, 10);
if (NavigationManager.Uri.EndsWith("login"))
NavigationManager.NavigateTo("/", true);
else
NavigationManager.NavigateTo(NavigationManager.Uri, true);
}
else
{
await InvokeAsync(StateHasChanged);
}
}
else
{
var token = await UserService.Login(LoginData.Email, LoginData.Password, TotpData.Code);
await CookieService.SetValue("token", token, 10); await CookieService.SetValue("token", token, 10);
if (NavigationManager.Uri.EndsWith("login")) if (NavigationManager.Uri.EndsWith("login"))
@@ -137,13 +156,14 @@
else else
NavigationManager.NavigateTo(NavigationManager.Uri, true); NavigationManager.NavigateTo(NavigationManager.Uri, true);
} }
else
{
await InvokeAsync(StateHasChanged);
}
} }
catch (DisplayException e) catch (DisplayException e)
{ {
// Reset state
LoginData = new();
TotpData = new();
TotpRequired = false;
await AlertService.Error( await AlertService.Error(
SmartTranslateService.Translate("Error"), SmartTranslateService.Translate("Error"),
SmartTranslateService.Translate(e.Message) SmartTranslateService.Translate(e.Message)
@@ -151,6 +171,11 @@
} }
catch (Exception e) catch (Exception e)
{ {
// Reset state
LoginData = new();
TotpData = new();
TotpRequired = false;
await AlertService.Error( await AlertService.Error(
SmartTranslateService.Translate("Error"), SmartTranslateService.Translate("Error"),
SmartTranslateService.Translate("An error occured while logging you in") SmartTranslateService.Translate("An error occured while logging you in")

View File

@@ -176,12 +176,22 @@
</div> </div>
<div class="modal-body scroll-y pt-10 pb-15 px-lg-17"> <div class="modal-body scroll-y pt-10 pb-15 px-lg-17">
<div class="text-gray-500 fw-semibold fs-6 mb-10"> <div class="text-gray-500 fw-semibold fs-6 mb-10">
To finish activating, enter the current TOTP code <br/> <div class="alert alert-primary d-flex align-items-center p-5 mb-10">
<input type="text" class="form-control form-control-lg form-control-solid" @bind="currentTotp"/> <i class="bx bx-info-circle fs-2hx text-primary me-4">
<span class="path1"></span><span class="path2"></span>
</i>
<div class="d-flex flex-column">
<h4 class="mb-1 text-primary">
<TL>2fa Code requiered</TL>
</h4>
<span>In order to finish the activation of 2fa, you need to enter the code your 2fa app shows you.</span>
</div>
</div>
<input type="text" class="form-control form-control-lg form-control-solid" placeholder="@SmartTranslateService.Translate("2fa Code")" @bind="currentTotp"/>
<br/> <br/>
<WButton CssClasses="btn btn-primary px-6 align-self-center text-nowrap float end" WorkingText="@SmartTranslateService.Translate("Saving")" Text="@SmartTranslateService.Translate("Save")" OnClick="CheckAndSaveTotp"> <WButton CssClasses="btn btn-primary px-6 align-self-center text-nowrap float-end" WorkingText="@SmartTranslateService.Translate("Saving")" Text="@SmartTranslateService.Translate("Finish")" OnClick="CheckAndSaveTotp">
</WButton> </WButton>
</div> </div>
</div> </div>
@@ -239,9 +249,10 @@
if (await TotpService.Verify(TotpSecret, currentTotp)) if (await TotpService.Verify(TotpSecret, currentTotp))
{ {
await TotpService.EnforceTotpLogin(); await TotpService.EnforceTotpLogin();
TotpEnabled = await TotpService.GetEnabled(); TotpEnabled = true;
TotpSecret = await TotpService.GetSecret(); TotpSecret = await TotpService.GetSecret();
await ToastService.Success("Successfully enabled 2fa!"); await ToastService.Success("Successfully enabled 2fa!");
StateHasChanged();
} }
else else
{ {