
Added functinality to always remember user login. Provides the option to force login cookie expiration and dont fallback on the session timespan that is used as default when users dont choose the option 'Remember me'.
339 lines
14 KiB
Plaintext
339 lines
14 KiB
Plaintext
@using System.Net
|
|
@namespace Oqtane.Modules.Admin.Login
|
|
@inherits ModuleBase
|
|
@inject NavigationManager NavigationManager
|
|
@inject IUserService UserService
|
|
@inject ISettingService SettingService
|
|
@inject IServiceProvider ServiceProvider
|
|
@inject IStringLocalizer<Index> Localizer
|
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
|
|
|
<AuthorizeView Roles="@RoleNames.Registered">
|
|
<Authorizing>
|
|
<text>...</text>
|
|
</Authorizing>
|
|
<NotAuthorized>
|
|
@if (!twofactor)
|
|
{
|
|
<form @ref="login" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
|
<div class="Oqtane-Modules-Admin-Login" @onkeypress="@(e => KeyPressed(e))">
|
|
@if (_allowexternallogin)
|
|
{
|
|
<button type="button" class="btn btn-primary" @onclick="ExternalLogin">@Localizer["Use"] @PageState.Site.Settings["ExternalLogin:ProviderName"]</button>
|
|
<br /><br />
|
|
}
|
|
@if (_allowsitelogin)
|
|
{
|
|
<div class="form-group">
|
|
<Label Class="control-label" For="username" HelpText="Please enter your Username" ResourceKey="Username">Username:</Label>
|
|
<input id="username" type="text" @ref="username" class="form-control" placeholder="@Localizer["Username.Placeholder"]" @bind="@_username" required />
|
|
</div>
|
|
<div class="form-group mt-2">
|
|
<Label Class="control-label" For="password" HelpText="Please enter your Password" ResourceKey="Password">Password:</Label>
|
|
<div class="input-group">
|
|
<input id="password" type="@_passwordtype" name="Password" class="form-control" placeholder="@Localizer["Password.Placeholder"]" @bind="@_password" required />
|
|
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword" tabindex="-1">@_togglepassword</button>
|
|
</div>
|
|
</div>
|
|
<div class="form-group mt-2">
|
|
@if (!_alwaysremember)
|
|
{
|
|
<div class="form-check">
|
|
<input id="remember" type="checkbox" class="form-check-input" @bind="@_remember" />
|
|
<Label Class="control-label" For="remember" HelpText="Specify if you would like to be signed back in automatically the next time you visit this site" ResourceKey="Remember">Remember Me?</Label>
|
|
</div>
|
|
}
|
|
</div>
|
|
<button type="button" class="btn btn-primary" @onclick="Login">@SharedLocalizer["Login"]</button>
|
|
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
|
<br /><br />
|
|
<button type="button" class="btn btn-secondary" @onclick="Forgot">@Localizer["ForgotPassword"]</button>
|
|
}
|
|
</div>
|
|
</form>
|
|
}
|
|
else
|
|
{
|
|
<form @ref="login" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
|
<div class="container Oqtane-Modules-Admin-Login">
|
|
<div class="form-group">
|
|
<Label Class="control-label" For="code" HelpText="Please enter the secure verification code which was sent to you by email" ResourceKey="Code">Verification Code:</Label>
|
|
<input id="code" class="form-control" @bind="@_code" placeholder="@Localizer["Code.Placeholder"]" maxlength="6" required />
|
|
</div>
|
|
<br />
|
|
<button type="button" class="btn btn-primary" @onclick="Login">@SharedLocalizer["Login"]</button>
|
|
<button type="button" class="btn btn-secondary" @onclick="Reset">@SharedLocalizer["Cancel"]</button>
|
|
</div>
|
|
</form>
|
|
}
|
|
</NotAuthorized>
|
|
</AuthorizeView>
|
|
|
|
@code {
|
|
private bool _allowsitelogin = true;
|
|
private bool _allowexternallogin = false;
|
|
private ElementReference login;
|
|
private bool validated = false;
|
|
private bool twofactor = false;
|
|
private string _username = string.Empty;
|
|
private ElementReference username;
|
|
private string _password = string.Empty;
|
|
private string _passwordtype = "password";
|
|
private string _togglepassword = string.Empty;
|
|
private bool _remember = false;
|
|
private bool _alwaysremember = false;
|
|
private string _code = string.Empty;
|
|
|
|
private string _returnUrl = string.Empty;
|
|
|
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
|
|
|
|
public override List<Resource> Resources => new List<Resource>()
|
|
{
|
|
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" }
|
|
};
|
|
|
|
protected override async Task OnInitializedAsync()
|
|
{
|
|
try
|
|
{
|
|
_allowexternallogin = (SettingService.GetSetting(PageState.Site.Settings, "ExternalLogin:ProviderType", "") != "") ? true : false;
|
|
_allowsitelogin = bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:AllowSiteLogin", "true"));
|
|
|
|
_togglepassword = SharedLocalizer["ShowPassword"];
|
|
|
|
if (PageState.QueryString.ContainsKey("returnurl"))
|
|
{
|
|
_returnUrl = PageState.QueryString["returnurl"];
|
|
}
|
|
|
|
if (PageState.QueryString.ContainsKey("name"))
|
|
{
|
|
_username = PageState.QueryString["name"];
|
|
}
|
|
|
|
if (PageState.QueryString.ContainsKey("token") && !string.IsNullOrEmpty(_username))
|
|
{
|
|
var user = new User();
|
|
user.SiteId = PageState.Site.SiteId;
|
|
user.Username = _username;
|
|
|
|
if (PageState.QueryString.ContainsKey("key"))
|
|
{
|
|
user = await UserService.LinkUserAsync(user, PageState.QueryString["token"], PageState.Site.Settings["ExternalLogin:ProviderType"], PageState.QueryString["key"], PageState.Site.Settings["ExternalLogin:ProviderName"]);
|
|
if (user != null)
|
|
{
|
|
await logger.LogInformation(LogFunction.Security, "External Login Linkage Successful For Username {Username}", _username);
|
|
AddModuleMessage(Localizer["Success.Account.Linked"], MessageType.Info);
|
|
}
|
|
else
|
|
{
|
|
await logger.LogError(LogFunction.Security, "External Login Linkage Failed For Username {Username}", _username);
|
|
AddModuleMessage(Localizer["Message.Account.NotLinked"], MessageType.Warning);
|
|
}
|
|
_username = "";
|
|
}
|
|
else
|
|
{
|
|
user = await UserService.VerifyEmailAsync(user, PageState.QueryString["token"]);
|
|
if (user != null)
|
|
{
|
|
await logger.LogInformation(LogFunction.Security, "Email Verified For For Username {Username}", _username);
|
|
AddModuleMessage(Localizer["Success.Account.Verified"], MessageType.Info);
|
|
}
|
|
else
|
|
{
|
|
await logger.LogError(LogFunction.Security, "Email Verification Failed For Username {Username}", _username);
|
|
AddModuleMessage(Localizer["Message.Account.NotVerified"], MessageType.Warning);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (PageState.QueryString.ContainsKey("status"))
|
|
{
|
|
AddModuleMessage(Localizer["ExternalLoginStatus." + PageState.QueryString["status"]], MessageType.Info);
|
|
}
|
|
}
|
|
if (PageState.Site.Settings.TryGetValue("LoginOptions:AlwaysRemember", out string alwaysRememberStr))
|
|
{
|
|
_alwaysremember = Convert.ToBoolean(alwaysRememberStr);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await logger.LogError(ex, "Error Loading Login {Error}", ex.Message);
|
|
AddModuleMessage(Localizer["Error.LoadLogin"], MessageType.Error);
|
|
}
|
|
}
|
|
|
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
|
{
|
|
if (firstRender && PageState.User == null && _allowsitelogin)
|
|
{
|
|
await username.FocusAsync();
|
|
}
|
|
|
|
// redirect logged in user to specified page
|
|
if (PageState.User != null)
|
|
{
|
|
NavigationManager.NavigateTo(PageState.ReturnUrl);
|
|
}
|
|
}
|
|
|
|
private async Task Login()
|
|
{
|
|
try
|
|
{
|
|
validated = true;
|
|
var interop = new Interop(JSRuntime);
|
|
if (await interop.FormValid(login))
|
|
{
|
|
var hybrid = (PageState.Runtime == Shared.Runtime.Hybrid);
|
|
var user = new User { SiteId = PageState.Site.SiteId, Username = _username, Password = _password, LastIPAddress = SiteState.RemoteIPAddress};
|
|
|
|
if (!twofactor)
|
|
{
|
|
bool alwaysRemember = false;
|
|
if (PageState.Site.Settings.TryGetValue("LoginOptions:AlwaysRemember", out string alwaysRememberStr))
|
|
{
|
|
alwaysRemember = Convert.ToBoolean(alwaysRememberStr);
|
|
}
|
|
bool remember = alwaysRemember || _remember;
|
|
_remember = remember;
|
|
user = await UserService.LoginUserAsync(user, hybrid, _remember);
|
|
}
|
|
else
|
|
{
|
|
user = await UserService.VerifyTwoFactorAsync(user, _code);
|
|
}
|
|
|
|
if (user.IsAuthenticated)
|
|
{
|
|
await logger.LogInformation(LogFunction.Security, "Login Successful For Username {Username}", _username);
|
|
|
|
if (hybrid)
|
|
{
|
|
// hybrid apps utilize an interactive login
|
|
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider));
|
|
authstateprovider.NotifyAuthenticationChanged();
|
|
NavigationManager.NavigateTo(NavigateUrl(WebUtility.UrlDecode(_returnUrl), true));
|
|
}
|
|
else
|
|
{
|
|
// post back to the Login page so that the cookies are set correctly
|
|
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, username = _username, password = _password, remember = _remember, returnurl = _returnUrl };
|
|
string url = Utilities.TenantUrl(PageState.Alias, "/pages/login/");
|
|
await interop.SubmitForm(url, fields);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:TwoFactor", "false") == "required" || user.TwoFactorRequired)
|
|
{
|
|
twofactor = true;
|
|
validated = false;
|
|
AddModuleMessage(Localizer["Message.TwoFactor"], MessageType.Info);
|
|
}
|
|
else
|
|
{
|
|
if (!twofactor)
|
|
{
|
|
await logger.LogInformation(LogFunction.Security, "Login Failed For Username {Username}", _username);
|
|
AddModuleMessage(Localizer["Error.Login.Fail"], MessageType.Error);
|
|
}
|
|
else
|
|
{
|
|
await logger.LogInformation(LogFunction.Security, "Two Factor Verification Failed For Username {Username}", _username);
|
|
AddModuleMessage(Localizer["Error.TwoFactor.Fail"], MessageType.Error);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AddModuleMessage(Localizer["Message.Required.UserInfo"], MessageType.Warning);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await logger.LogError(ex, "Error Performing Login {Error}", ex.Message);
|
|
AddModuleMessage(Localizer["Error.Login"], MessageType.Error);
|
|
}
|
|
}
|
|
|
|
private void Cancel()
|
|
{
|
|
NavigationManager.NavigateTo(_returnUrl);
|
|
}
|
|
|
|
private async Task Forgot()
|
|
{
|
|
try
|
|
{
|
|
if (_username != string.Empty)
|
|
{
|
|
var user = await UserService.GetUserAsync(_username, PageState.Site.SiteId);
|
|
if (user != null)
|
|
{
|
|
await UserService.ForgotPasswordAsync(user);
|
|
await logger.LogInformation(LogFunction.Security, "Password Reset Notification Sent For Username {Username}", _username);
|
|
AddModuleMessage(Localizer["Message.ForgotUser"], MessageType.Info);
|
|
}
|
|
else
|
|
{
|
|
AddModuleMessage(Localizer["Message.UserDoesNotExist"], MessageType.Warning);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AddModuleMessage(Localizer["Message.ForgotPassword"], MessageType.Info);
|
|
}
|
|
|
|
StateHasChanged();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await logger.LogError(ex, "Error Resetting Password {Error}", ex.Message);
|
|
AddModuleMessage(Localizer["Error.ResetPassword"], MessageType.Error);
|
|
}
|
|
}
|
|
|
|
private void Reset()
|
|
{
|
|
twofactor = false;
|
|
_username = "";
|
|
_password = "";
|
|
ClearModuleMessage();
|
|
StateHasChanged();
|
|
}
|
|
|
|
private async Task KeyPressed(KeyboardEventArgs e)
|
|
{
|
|
if (e.Code == "Enter" || e.Code == "NumpadEnter")
|
|
{
|
|
await Login();
|
|
}
|
|
}
|
|
|
|
private void TogglePassword()
|
|
{
|
|
if (_passwordtype == "password")
|
|
{
|
|
_passwordtype = "text";
|
|
_togglepassword = SharedLocalizer["HidePassword"];
|
|
}
|
|
else
|
|
{
|
|
_passwordtype = "password";
|
|
_togglepassword = SharedLocalizer["ShowPassword"];
|
|
}
|
|
}
|
|
|
|
private void ExternalLogin()
|
|
{
|
|
NavigationManager.NavigateTo(Utilities.TenantUrl(PageState.Alias, "/pages/external?returnurl=" + _returnUrl), true);
|
|
}
|
|
|
|
}
|