Add project files.

This commit is contained in:
Khwezi Mngoma
2026-06-05 22:07:25 +02:00
parent 59ea7de742
commit 437e8c5c08
978 changed files with 151911 additions and 0 deletions
@@ -0,0 +1,16 @@
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
@{
ViewData["Title"] = Localizer["Title"];
}
<div class="py-8">
<h1 class="text-4xl font-bold mb-6">@ViewData["Title"]</h1>
<div class="card">
<div class="card-content">
<p class="text-lg">
@Localizer["ConfirmMessage"]
</p>
</div>
</div>
</div>
@@ -0,0 +1,51 @@
@inject IViewLocalizer Localizer
@using Microsoft.AspNetCore.Mvc.Localization
@model LiteCharmsSecurity.STS.Identity.ViewModels.Account.ExternalLoginConfirmationViewModel
@{
ViewData["Title"] = Localizer["Title"];
}
<div class="py-8">
<h1 class="text-4xl font-bold mb-2">@ViewData["Title"]</h1>
<h2 class="text-2xl text-muted-foreground mb-6">@Localizer["SubTitle", ViewData["LoginProvider"]]</h2>
@await Html.PartialAsync("_ValidationSummary")
<div class="alert-default">
<lucideicon name="info" class="h-4 w-4" />
<div>
<div class="alert-description">
@Localizer["Info", ViewData["LoginProvider"]]
</div>
</div>
</div>
<div class="card">
<div class="card-content">
<form asp-controller="Account" asp-action="ExternalLoginConfirmation" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="space-y-6">
<div class="form-item">
<label asp-for="UserName" class="form-label">
<lucideicon name="user" class="inline-block h-4 w-4 mr-1" />
@Localizer["UserName"]
</label>
<input asp-for="UserName" class="input" tw-validation />
<span asp-validation-for="UserName" class="form-message"></span>
</div>
<div class="form-item">
<label asp-for="Email" class="form-label">
<lucideicon name="mail" class="inline-block h-4 w-4 mr-1" />
@Localizer["Email"]
</label>
<input asp-for="Email" class="input" tw-validation />
<span asp-validation-for="Email" class="form-message"></span>
</div>
<div>
<button type="submit" class="btn-default">
<lucideicon name="user-plus" class="inline-block h-4 w-4 mr-2" />
@Localizer["Register"]
</button>
</div>
</form>
</div>
</div>
</div>
@@ -0,0 +1,15 @@
@inject IViewLocalizer Localizer
@using Microsoft.AspNetCore.Mvc.Localization
@{
ViewData["Title"] = Localizer["Title"];
}
<div class="py-8">
<h1 class="text-4xl font-bold mb-2">@ViewData["Title"]</h1>
<div class="alert-destructive">
<lucideicon name="alert-circle" class="h-4 w-4" />
<div>
<div class="alert-description">@Localizer["Error"]</div>
</div>
</div>
</div>
@@ -0,0 +1,85 @@
@using Microsoft.AspNetCore.Identity
@using Microsoft.AspNetCore.Mvc.Localization
@using Microsoft.Extensions.Options
@inject IViewLocalizer Localizer
@model LiteCharmsSecurity.STS.Identity.ViewModels.Account.ForgotPasswordViewModel
@inject IOptions<IdentityOptions> IdentityOptions
@{
ViewData["Title"] = Localizer["Title"];
}
<div class="py-8">
<h1 class="text-4xl font-bold mb-2">@ViewData["Title"]</h1>
@await Html.PartialAsync("_ValidationSummary")
<div class="card mt-6">
<div class="card-header">
<h2 class="card-title">@Localizer["ChoosePolicy"]</h2>
</div>
<div class="card-content">
<form asp-controller="Account" asp-action="ForgotPassword" method="post" class="space-y-6">
<div class="form-item">
<div class="flex space-x-6">
@if (IdentityOptions.Value.User.RequireUniqueEmail)
{
<div class="flex items-center space-x-2">
<input type="radio" name="Policy" asp-for="Policy" value="Email" id="policy-email" class="radio" checked />
<label for="policy-email" class="label cursor-pointer">Email</label>
</div>
}
@if (IdentityOptions.Value.User.RequireUniqueEmail)
{
<div class="flex items-center space-x-2">
<input type="radio" name="Policy" asp-for="Policy" value="Username" id="policy-username" class="radio" />
<label for="policy-username" class="label cursor-pointer">Username</label>
</div>
}
else
{
<div class="flex items-center space-x-2">
<input type="radio" name="Policy" asp-for="Policy" value="Username" id="policy-username" class="radio" checked />
<label for="policy-username" class="label cursor-pointer">Username</label>
</div>
}
</div>
</div>
<div id="resetPasswordByEmail" class="resetPasswordBy @(IdentityOptions.Value.User.RequireUniqueEmail ? "" : "hidden")">
<div class="form-item">
<label asp-for="Email" class="form-label">
<lucideicon name="mail" class="inline-block h-4 w-4 mr-1" />
@Localizer["Email"]
</label>
<input asp-for="Email" type="email" class="input" tw-validation required />
<span asp-validation-for="Email" class="form-message"></span>
</div>
<button type="submit" class="btn-default mt-3">
<lucideicon name="send" class="inline-block h-4 w-4 mr-2" />
@Localizer["Submit"]
</button>
</div>
<div id="resetPasswordByUsername" class="resetPasswordBy @(!IdentityOptions.Value.User.RequireUniqueEmail ? "" : "hidden")">
<div class="form-item">
<label asp-for="Username" class="form-label">
<lucideicon name="user" class="inline-block h-4 w-4 mr-1" />
@Localizer["Username"]
</label>
<input asp-for="Username" class="input" tw-validation required />
<span asp-validation-for="Username" class="form-message"></span>
</div>
<button type="submit" class="btn-default mt-3">
<lucideicon name="send" class="inline-block h-4 w-4 mr-2" />
@Localizer["Submit"]
</button>
</div>
</form>
</div>
</div>
</div>
@section scripts {
<script src="~/js/forgot-password.js"></script>
}
@@ -0,0 +1,18 @@
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
@{
ViewData["Title"] = Localizer["Title"];
}
<div class="py-8">
<h1 class="text-4xl font-bold mb-6">@ViewData["Title"]</h1>
<div class="alert-default">
<lucideicon name="check" class="h-4 w-4" />
<div>
<div class="alert-description">
@Localizer["CheckEmailMessage"]
</div>
</div>
</div>
</div>
@@ -0,0 +1,10 @@
@inject IViewLocalizer Localizer
@using Microsoft.AspNetCore.Mvc.Localization
@{
ViewData["Title"] = Localizer["Title"];
}
<div class="py-8">
<h1 class="text-4xl font-bold text-destructive mb-2">@ViewData["Title"]</h1>
<p class="text-lg text-destructive">@Localizer["Info"]</p>
</div>
@@ -0,0 +1,49 @@
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
@model LiteCharmsSecurity.STS.Identity.ViewModels.Account.LoggedOutViewModel
@{
// set this so the layout rendering sees an anonymous user
ViewData["signed-out"] = true;
}
<div class="py-8">
<div class="flex min-h-[50vh] items-center justify-center">
<div class="w-full max-w-md">
<div class="mb-6 text-center">
<div class="mx-auto mb-4 inline-flex h-16 w-16 items-center justify-center rounded-full bg-primary/10 ring-1 ring-primary/20">
<lucideicon name="check-circle" class="h-10 w-10 text-primary" />
</div>
<h1 class="text-3xl font-bold mb-2">@Localizer["Title"]</h1>
<p class="text-muted-foreground">@Localizer["SubTitle"]</p>
</div>
@if (Model.PostLogoutRedirectUri != null)
{
<div class="card">
<div class="card-content py-6">
<p class="text-center">
@Localizer["Click"]
<a class="text-primary hover:underline font-medium PostLogoutRedirectUri" href="@Model.PostLogoutRedirectUri">@Localizer["Here"]</a>
@Localizer["Return"]
<span class="font-semibold">@Model.ClientName</span>.
</p>
</div>
</div>
}
</div>
</div>
@if (Model.SignOutIframeUrl != null)
{
<iframe width="0" height="0" class="signout" src="@Model.SignOutIframeUrl"></iframe>
}
</div>
@section scripts
{
@if (Model.AutomaticRedirectAfterSignOut)
{
<script src="~/js/signout-redirect.js"></script>
}
}
@@ -0,0 +1,168 @@
@using Microsoft.AspNetCore.Mvc.Localization
@using LiteCharmsSecurity.STS.Identity.Configuration.Interfaces
@using LiteCharmsSecurity.STS.Identity.Helpers.Localization
@using LiteCharmsSecurity.STS.Identity.Passkeys
@inject IViewLocalizer Localizer
@model LiteCharmsSecurity.STS.Identity.ViewModels.Account.LoginViewModel
@inject IRootConfiguration RootConfiguration
<div class="bg-background flex min-h-[calc(100vh-300px)] flex-col items-center justify-center gap-6 py-8 px-6 md:px-10">
<div class="w-full max-w-md">
<div class="flex flex-col items-center gap-2 text-center mb-6">
<a href="/" class="flex flex-col items-center gap-2 font-medium">
<div class="flex h-20 w-20 items-center justify-center rounded-md">
<img src="@UrlHelper.Content(RootConfiguration.AdminConfiguration.HomePageLogoUri)" alt="Logo"
class="h-16 w-auto opacity-90 dark:invert" />
</div>
<span class="sr-only">@RootConfiguration.AdminConfiguration.PageTitle</span>
</a>
<h1 class="text-2xl font-bold">@Localizer["Title"]</h1>
@if (RootConfiguration.RegisterConfiguration.Enabled)
{
<p class="text-sm text-muted-foreground">
@Localizer["NoAccount"]
<a asp-action="Register" asp-controller="Account"
class="text-primary hover:underline font-medium">@Localizer["SignUp"]</a>
</p>
}
</div>
<div class="card">
<div class="card-content py-6">
@if (Model.EnableLocalLogin)
{
<form asp-route="Login" class="space-y-5">
<input type="hidden" asp-for="ReturnUrl" />
@await Html.PartialAsync("_ValidationSummary")
<div class="form-item">
<label class="form-label" for="Username">
<lucideicon name="user" class="inline-block h-4 w-4 mr-1" />
@Localizer[LoginPolicyResolutionLocalizer.GetUserNameLocalizationKey(Model.LoginResolutionPolicy)]
</label>
<input id="Username" class="input" asp-for="Username" autofocus tw-validation />
</div>
<div class="form-item">
<label class="form-label" for="Password">
<lucideicon name="lock" class="inline-block h-4 w-4 mr-1" />
@Localizer["Password"]
</label>
<input id="Password" type="password" class="input" asp-for="Password" autocomplete="off"
tw-validation />
</div>
@if (Model.AllowRememberLogin)
{
<div class="flex items-center space-x-2">
<input asp-for="RememberLogin" type="checkbox" class="checkbox" id="RememberLogin" />
<label for="RememberLogin" class="label cursor-pointer text-sm">
@Localizer["Remember"]
</label>
</div>
}
<div class="form-item">
<button type="submit" name="button" value="login" class="btn-default w-full">
<lucideicon name="log-in" class="inline-block h-4 w-4 mr-2" />
@Localizer["Login"]
</button>
</div>
<div class="flex justify-between text-sm">
<a asp-action="ForgotPassword" asp-controller="Account"
class="text-primary hover:underline inline-flex items-center gap-1">
<lucideicon name="key" class="h-3 w-3" />
@Localizer["Forgot"]
</a>
<button type="submit" name="button" value="cancel"
class="text-primary hover:underline inline-flex items-center gap-1">
<lucideicon name="x" class="h-3 w-3" />
@Localizer["Cancel"]
</button>
</div>
<div class="relative">
<div class="absolute inset-0 flex items-center">
<span class="w-full border-t border-border"></span>
</div>
<div class="relative flex justify-center text-xs uppercase">
<span class="bg-background px-2 text-muted-foreground">@Localizer["Or"]</span>
</div>
</div>
<div class="form-item">
<passkey-submit operation="@PasskeyOperation.Request" name="Passkey" email-name="Username" class="btn-outline w-full" formnovalidate>
<lucideicon name="fingerprint" class="inline-block h-4 w-4 mr-2" />
@Localizer["LoginWithPasskey"]
</passkey-submit>
</div>
@if (Model.VisibleExternalProviders.Any())
{
<div class="relative">
<div class="absolute inset-0 flex items-center">
<span class="w-full border-t border-border"></span>
</div>
<div class="relative flex justify-center text-xs uppercase">
<span class="bg-background px-2 text-muted-foreground">@Localizer["Or"]</span>
</div>
</div>
<div class="grid gap-4 @(Model.VisibleExternalProviders.Count() > 1 ? "sm:grid-cols-2" : "")">
@foreach (var provider in Model.VisibleExternalProviders)
{
var providerName = provider.DisplayName?.ToLower() ?? provider.AuthenticationScheme.ToLower();
<a asp-action="ExternalLogin" asp-route-provider="@provider.AuthenticationScheme"
asp-route-returnUrl="@Model.ReturnUrl"
class="btn-outline flex items-center justify-center gap-2">
<span class="text-sm">@provider.DisplayName</span>
</a>
}
</div>
}
</form>
}
@if (!Model.EnableLocalLogin && Model.VisibleExternalProviders.Any())
{
@await Html.PartialAsync("_ValidationSummary")
<div class="grid gap-4 @(Model.VisibleExternalProviders.Count() > 1 ? "sm:grid-cols-2" : "")">
@foreach (var provider in Model.VisibleExternalProviders)
{
var providerName = provider.DisplayName?.ToLower() ?? provider.AuthenticationScheme.ToLower();
<a asp-action="ExternalLogin" asp-route-provider="@provider.AuthenticationScheme"
asp-route-returnUrl="@Model.ReturnUrl"
class="btn-outline flex items-center justify-center gap-2">
<span class="text-sm">@provider.DisplayName</span>
</a>
}
</div>
}
@if (!Model.EnableLocalLogin && !Model.VisibleExternalProviders.Any())
{
<div class="alert-destructive">
<lucideicon name="alert-circle" class="h-4 w-4" />
<div>
<h5 class="alert-title">@Localizer["InvalidRequest"]</h5>
<div class="alert-description">@Localizer["NoSchema"]</div>
</div>
</div>
}
</div>
</div>
<div class="mt-4 text-center">
<p class="text-xs text-muted-foreground">
@Localizer["PrivacyNotice"]
</p>
</div>
</div>
</div>
@section scripts {
<script src="~/js/passkey-submit.js" asp-append-version="true"></script>
}
@@ -0,0 +1,51 @@
@inject IViewLocalizer Localizer
@using Microsoft.AspNetCore.Mvc.Localization
@model LiteCharmsSecurity.STS.Identity.ViewModels.Account.LoginWith2faViewModel
@{
ViewData["Title"] = Localizer["Title"];
}
<div class="py-8">
<h1 class="text-4xl font-bold mb-2">@ViewData["Title"]</h1>
@await Html.PartialAsync("_ValidationSummary")
<div class="card mt-6 max-w-md">
<div class="card-header">
<p class="card-description">@Localizer["Info"]</p>
</div>
<div class="card-content">
<form method="post" asp-route-returnUrl="@Model.ReturnUrl" class="space-y-6">
<input asp-for="RememberMe" type="hidden" />
<input asp-for="ReturnUrl" type="hidden" />
<div class="form-item">
<label asp-for="TwoFactorCode" class="form-label">
<lucideicon name="shield-check" class="inline-block h-4 w-4 mr-1" />
@Localizer["Code"]
</label>
<input asp-for="TwoFactorCode" class="input" autocomplete="off" autofocus tw-validation />
<span asp-validation-for="TwoFactorCode" class="form-message"></span>
</div>
<div class="form-item">
<div class="flex items-center space-x-2">
<input asp-for="RememberMachine" type="checkbox" class="checkbox" id="RememberMachine" />
<label for="RememberMachine" class="label cursor-pointer">
@Localizer["Remember"]
</label>
</div>
</div>
<div>
<button type="submit" class="btn-default">
<lucideicon name="log-in" class="inline-block h-4 w-4 mr-2" />
@Localizer["LogIn"]
</button>
</div>
</form>
</div>
</div>
<p class="mt-6">
@Localizer["NoAuthenticatorDevice"]
<a class="text-primary hover:underline" asp-action="LoginWithRecoveryCode" asp-route-returnUrl="@Model.ReturnUrl">@Localizer["LoginWithRecoveryCode"]</a>
</p>
</div>
@@ -0,0 +1,35 @@
@inject IViewLocalizer Localizer
@using Microsoft.AspNetCore.Mvc.Localization
@model LiteCharmsSecurity.STS.Identity.ViewModels.Account.LoginWithRecoveryCodeViewModel
@{
ViewData["Title"] = Localizer["Title"];
}
<div class="py-8">
<h1 class="text-4xl font-bold mb-2">@ViewData["Title"]</h1>
@await Html.PartialAsync("_ValidationSummary")
<div class="card mt-6 max-w-md">
<div class="card-header">
<p class="card-description">@Localizer["Info"]</p>
</div>
<div class="card-content">
<form method="post" class="space-y-6">
<input asp-for="ReturnUrl" type="hidden" />
<div class="form-item">
<label asp-for="RecoveryCode" class="form-label">
<lucideicon name="key" class="inline-block h-4 w-4 mr-1" />
@Localizer["Code"]
</label>
<input asp-for="RecoveryCode" class="input" autocomplete="off" autofocus tw-validation />
<span asp-validation-for="RecoveryCode" class="form-message"></span>
</div>
<button type="submit" class="btn-default">
<lucideicon name="log-in" class="inline-block h-4 w-4 mr-2" />
@Localizer["LogIn"]
</button>
</form>
</div>
</div>
</div>
@@ -0,0 +1,26 @@
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
@model LiteCharmsSecurity.STS.Identity.ViewModels.Account.LogoutViewModel
<div class="py-8">
<div class="flex min-h-[50vh] items-center justify-center">
<div class="w-full max-w-md">
<div class="mb-6 text-center">
<h1 class="text-3xl font-bold mb-2">@Localizer["Title"]</h1>
</div>
<div class="card">
<div class="card-content py-8">
<p class="mb-6 text-center text-muted-foreground">@Localizer["Message"]</p>
<form asp-action="Logout" class="flex justify-center">
<input type="hidden" name="logoutId" value="@Model.LogoutId" />
<button class="btn-default">
<lucideicon name="log-out" class="inline-block h-4 w-4 mr-2" />
@Localizer["Yes"]
</button>
</form>
</div>
</div>
</div>
</div>
</div>
@@ -0,0 +1,60 @@
@inject IViewLocalizer Localizer
@using Microsoft.AspNetCore.Mvc.Localization
@model LiteCharmsSecurity.STS.Identity.ViewModels.Account.RegisterViewModel
@{
ViewData["Title"] = Localizer["Title"];
}
<div class="py-8">
<h1 class="text-4xl font-bold mb-2">@ViewData["Title"]</h1>
@await Html.PartialAsync("_ValidationSummary")
<div class="card mt-6">
<div class="card-header">
<h2 class="card-title">@Localizer["SubTitle"]</h2>
</div>
<div class="card-content">
<form asp-controller="Account" asp-action="Register" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="space-y-6">
<div class="form-item">
<label asp-for="UserName" class="form-label">
<lucideicon name="user" class="inline-block h-4 w-4 mr-1" />
@Localizer["UserName"]
</label>
<input asp-for="UserName" class="input" tw-validation />
<span asp-validation-for="UserName" class="form-message"></span>
</div>
<div class="form-item">
<label asp-for="Email" class="form-label">
<lucideicon name="mail" class="inline-block h-4 w-4 mr-1" />
@Localizer["Email"]
</label>
<input asp-for="Email" class="input" tw-validation />
<span asp-validation-for="Email" class="form-message"></span>
</div>
<div class="form-item">
<label asp-for="Password" class="form-label">
<lucideicon name="lock" class="inline-block h-4 w-4 mr-1" />
@Localizer["Password"]
</label>
<input asp-for="Password" type="password" class="input" tw-validation />
<span asp-validation-for="Password" class="form-message"></span>
</div>
<div class="form-item">
<label asp-for="ConfirmPassword" class="form-label">
<lucideicon name="lock-keyhole" class="inline-block h-4 w-4 mr-1" />
@Localizer["ConfirmPassword"]
</label>
<input asp-for="ConfirmPassword" type="password" class="input" tw-validation />
<span asp-validation-for="ConfirmPassword" class="form-message"></span>
</div>
<div>
<button type="submit" class="btn-default">
<lucideicon name="user-plus" class="inline-block h-4 w-4 mr-2" />
@Localizer["Button"]
</button>
</div>
</form>
</div>
</div>
</div>
@@ -0,0 +1,19 @@
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
@model LiteCharmsSecurity.STS.Identity.ViewModels.Account.RegisterConfirmationViewModel
@{
ViewData["Title"] = Localizer["ConfirmEmailTitle"];
}
<div class="py-8">
<h1 class="text-4xl font-bold mb-6">@ViewData["Title"]</h1>
<div class="alert-default">
<lucideicon name="check" class="h-4 w-4" />
<div>
<div class="alert-description">
@Localizer["VerificationSent"]
</div>
</div>
</div>
</div>
@@ -0,0 +1,17 @@
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
@{
ViewData["Title"] = Localizer["Title"];
}
<div class="py-8">
<h1 class="text-4xl font-bold mb-6">@ViewData["Title"]</h1>
<div class="alert-destructive">
<lucideicon name="alert-circle" class="h-4 w-4" />
<div>
<div class="alert-description">
@Localizer["Error"]
</div>
</div>
</div>
</div>
@@ -0,0 +1,52 @@
@inject IViewLocalizer Localizer
@using Microsoft.AspNetCore.Mvc.Localization
@model LiteCharmsSecurity.STS.Identity.ViewModels.Account.RegisterWithoutUsernameViewModel
@{
ViewData["Title"] = Localizer["Title"];
}
<div class="py-8">
<h1 class="text-4xl font-bold mb-2">@ViewData["Title"]</h1>
@await Html.PartialAsync("_ValidationSummary")
<div class="card mt-6">
<div class="card-header">
<h2 class="card-title">@Localizer["SubTitle"]</h2>
</div>
<div class="card-content">
<form asp-controller="Account" asp-action="RegisterWithoutUsername" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="space-y-6">
<div class="form-item">
<label asp-for="Email" class="form-label">
<lucideicon name="mail" class="inline-block h-4 w-4 mr-1" />
@Localizer["Email"]
</label>
<input asp-for="Email" class="input" tw-validation />
<span asp-validation-for="Email" class="form-message"></span>
</div>
<div class="form-item">
<label asp-for="Password" class="form-label">
<lucideicon name="lock" class="inline-block h-4 w-4 mr-1" />
@Localizer["Password"]
</label>
<input asp-for="Password" type="password" class="input" tw-validation />
<span asp-validation-for="Password" class="form-message"></span>
</div>
<div class="form-item">
<label asp-for="ConfirmPassword" class="form-label">
<lucideicon name="lock-keyhole" class="inline-block h-4 w-4 mr-1" />
@Localizer["ConfirmPassword"]
</label>
<input asp-for="ConfirmPassword" type="password" class="input" tw-validation />
<span asp-validation-for="ConfirmPassword" class="form-message"></span>
</div>
<div>
<button type="submit" class="btn-default">
<lucideicon name="user-plus" class="inline-block h-4 w-4 mr-2" />
@Localizer["Button"]
</button>
</div>
</form>
</div>
</div>
</div>
@@ -0,0 +1,53 @@
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
@model LiteCharmsSecurity.STS.Identity.ViewModels.Account.ResetPasswordViewModel
@{
ViewData["Title"] = Localizer["Title"];
}
<div class="py-8">
<h1 class="text-4xl font-bold mb-2">@ViewData["Title"]</h1>
@await Html.PartialAsync("_ValidationSummary")
<div class="card mt-6">
<div class="card-header">
<h2 class="card-title">@Localizer["SubTitle"]</h2>
</div>
<div class="card-content">
<form asp-controller="Account" asp-action="ResetPassword" method="post" class="space-y-6">
<input asp-for="Code" type="hidden" />
<div class="form-item">
<label asp-for="Email" class="form-label">
<lucideicon name="mail" class="inline-block h-4 w-4 mr-1" />
@Localizer["Email"]
</label>
<input asp-for="Email" class="input" tw-validation />
<span asp-validation-for="Email" class="form-message"></span>
</div>
<div class="form-item">
<label asp-for="Password" class="form-label">
<lucideicon name="lock" class="inline-block h-4 w-4 mr-1" />
@Localizer["Password"]
</label>
<input asp-for="Password" type="password" class="input" tw-validation />
<span asp-validation-for="Password" class="form-message"></span>
</div>
<div class="form-item">
<label asp-for="ConfirmPassword" class="form-label">
<lucideicon name="lock-keyhole" class="inline-block h-4 w-4 mr-1" />
@Localizer["ConfirmPassword"]
</label>
<input asp-for="ConfirmPassword" type="password" class="input" tw-validation />
<span asp-validation-for="ConfirmPassword" class="form-message"></span>
</div>
<div>
<button type="submit" class="btn-default">
<lucideicon name="refresh-cw" class="inline-block h-4 w-4 mr-2" />
@Localizer["Reset"]
</button>
</div>
</form>
</div>
</div>
</div>
@@ -0,0 +1,12 @@
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
@{
ViewData["Title"] = Localizer["Title"];
}
<div class="py-8">
<h1 class="text-4xl font-bold mb-6">@ViewData["Title"]</h1>
<p class="text-lg">
@Localizer["ConfirmMessage"] <a class="text-primary hover:underline" asp-controller="Account" asp-action="Login">@Localizer["Login"]</a>.
</p>
</div>
@@ -0,0 +1,111 @@
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
@model LiteCharmsSecurity.STS.Identity.ViewModels.Consent.ConsentViewModel
<div class="py-8">
<div class="mb-8">
@if (Model.ClientLogoUrl != null)
{
<div class="mb-4"><img src="@Model.ClientLogoUrl" alt="Client Logo" class="h-16"></div>
}
<h1 class="text-4xl font-bold mb-2">@Model.ClientName</h1>
<p class="text-lg text-muted-foreground">@Localizer["SubTitle"]</p>
</div>
@await Html.PartialAsync("_ValidationSummary")
<form asp-action="Index" class="space-y-6">
<input type="hidden" asp-for="ReturnUrl" />
<div class="text-lg mb-6">@Localizer["UnCheckPermission"]</div>
@if (Model.IdentityScopes.Any())
{
<div class="card">
<div class="card-header">
<h2 class="card-title flex items-center gap-2">
<lucideicon name="user" class="h-5 w-5" />
@Localizer["PersonalInformation"]
</h2>
</div>
<div class="card-content py-4">
<ul class="space-y-4">
@foreach (var scope in Model.IdentityScopes)
{
@await Html.PartialAsync("_ScopeListItem", scope)
}
</ul>
</div>
</div>
}
@if (Model.ApiScopes.Any())
{
<div class="card">
<div class="card-header">
<h2 class="card-title flex items-center gap-2">
<lucideicon name="lock" class="h-5 w-5" />
@Localizer["ApplicationAccess"]
</h2>
</div>
<div class="card-content py-4">
<ul class="space-y-4">
@foreach (var scope in Model.ApiScopes)
{
@await Html.PartialAsync("_ScopeListItem", scope)
}
</ul>
</div>
</div>
}
<div class="card">
<div class="card-header">
<h2 class="card-title flex items-center gap-2">
<lucideicon name="pencil" class="h-5 w-5" />
@Localizer["DescriptionTitle"]
</h2>
</div>
<div class="card-content py-4">
<input asp-for="Description" class="input w-full" placeholder="@Localizer["DescriptionPlaceholder"]" />
</div>
</div>
@if (Model.AllowRememberConsent)
{
<div class="card">
<div class="card-content py-4">
<div class="flex items-center justify-between">
<label for="RememberConsent" class="font-semibold cursor-pointer">
@Localizer["Remember"]
</label>
<label class="switch">
<input asp-for="RememberConsent" type="checkbox" id="RememberConsent" />
<span class="switch-slider"></span>
</label>
</div>
</div>
</div>
}
<div class="flex flex-wrap gap-4 items-center justify-between">
<div class="flex gap-3">
<button name="button" value="yes" class="btn-default" autofocus>
<lucideicon name="check" class="inline-block h-4 w-4 mr-2" />
@Localizer["Yes"]
</button>
<button name="button" value="no" class="btn-outline">
<lucideicon name="x" class="inline-block h-4 w-4 mr-2" />
@Localizer["No"]
</button>
</div>
@if (Model.ClientUrl != null)
{
<a class="btn-outline" target="_blank" href="@Model.ClientUrl">
<lucideicon name="info" class="h-4 w-4 mr-1" />
<strong>@Model.ClientName</strong>
</a>
}
</div>
</form>
</div>
@@ -0,0 +1,11 @@
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
<div class="py-8">
<div class="alert-default">
<lucideicon name="check" class="h-4 w-4" />
<div>
<h3 class="alert-title">@Localizer["Success"]</h3>
<div class="alert-description">@Localizer["SuccessMessage"]</div>
</div>
</div>
</div>
@@ -0,0 +1,27 @@
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
@model string
<div class="py-8">
<div class="card max-w-md">
<div class="card-header">
<h2 class="card-title">@Localizer["Title"]</h2>
</div>
<div class="card-content">
@await Html.PartialAsync("_ValidationSummary")
<form asp-action="UserCodeCapture" method="post" class="space-y-4">
<div class="form-item">
<label for="userCode" class="form-label">
<lucideicon name="smartphone" class="inline-block h-4 w-4 mr-1" />
@Localizer["EnterCode"]
</label>
<input id="userCode" name="userCode" placeholder="@Localizer["EnterCode"]" class="input" tw-validation />
</div>
<button class="btn-default w-full">
<lucideicon name="arrow-right" class="inline-block h-4 w-4 mr-2" />
@Localizer["Submit"]
</button>
</form>
</div>
</div>
</div>
@@ -0,0 +1,104 @@
@model LiteCharmsSecurity.STS.Identity.ViewModels.Device.DeviceAuthorizationViewModel
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
<div class="py-8 space-y-6">
<div class="space-y-2">
@if (Model.ClientLogoUrl != null)
{
<div class="h-16 w-auto">
<img src="@Model.ClientLogoUrl" class="h-16 object-contain" alt="@Model.ClientName" />
</div>
}
<h1 class="text-3xl font-bold">@Model.ClientName</h1>
<p class="text-muted-foreground">@Localizer["SubTitle"]</p>
@if (Model.ConfirmUserCode)
{
<p class="text-sm text-muted-foreground">@Localizer["ConfirmCode"] "@Model.UserCode".</p>
}
</div>
@await Html.PartialAsync("_ValidationSummary")
<form asp-action="Callback" class="space-y-6">
<input type="hidden" asp-for="UserCode" />
<div class="text-lg font-semibold">@Localizer["UnCheckPermission"]</div>
@if (Model.IdentityScopes.Any())
{
<div class="card">
<div class="card-header">
<h2 class="card-title flex items-center gap-2">
<lucideicon name="user" class="h-5 w-5" />
@Localizer["PersonalInformation"]
</h2>
</div>
<div class="card-content">
<ul class="space-y-2">
@foreach (var scope in Model.IdentityScopes)
{
@await Html.PartialAsync("_ScopeListItem", scope)
}
</ul>
</div>
</div>
}
@if (Model.ApiScopes.Any())
{
<div class="card">
<div class="card-header">
<h2 class="card-title flex items-center gap-2">
<lucideicon name="lock" class="h-5 w-5" />
@Localizer["ApplicationAccess"]
</h2>
</div>
<div class="card-content">
<ul class="space-y-2">
@foreach (var scope in Model.ApiScopes)
{
@await Html.PartialAsync("_ScopeListItem", scope)
}
</ul>
</div>
</div>
}
@if (Model.AllowRememberConsent)
{
<div class="card">
<div class="card-content">
<div class="flex items-center space-x-2">
<input asp-for="RememberConsent" type="checkbox" class="checkbox" id="RememberConsent" />
<label for="RememberConsent" class="label cursor-pointer font-semibold">
@Localizer["Remember"]
</label>
</div>
</div>
</div>
}
<div class="flex flex-wrap items-center justify-between gap-4">
<div class="flex gap-3">
<button name="button" value="yes" class="btn-default" autofocus>
<lucideicon name="check" class="inline-block h-4 w-4 mr-2" />
@Localizer["Yes"]
</button>
<button name="button" value="no" class="btn-outline">
<lucideicon name="x" class="inline-block h-4 w-4 mr-2" />
@Localizer["No"]
</button>
</div>
@if (Model.ClientUrl != null)
{
<a class="btn-outline" target="_blank" href="@Model.ClientUrl">
<lucideicon name="info" class="inline-block h-4 w-4 mr-1" />
@Localizer["Client"]
<strong>@Model.ClientName</strong>
</a>
}
</div>
</form>
</div>
@@ -0,0 +1,60 @@
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
@model LiteCharmsSecurity.STS.Identity.ViewModels.Diagnostics.DiagnosticsViewModel
<div class="py-8">
<h1 class="text-4xl font-bold mb-8">@Localizer["Title"]</h1>
<div class="space-y-8">
<div class="card">
<div class="card-header">
<h2 class="card-title">@Localizer["Claims"]</h2>
</div>
<div class="card-content">
<dl class="grid grid-cols-1 gap-4">
@foreach (var claim in Model.AuthenticateResult.Principal.Claims)
{
<div class="border-b pb-2">
<dt class="font-semibold text-sm mb-1">@claim.Type</dt>
<dd class="text-sm text-muted-foreground">@claim.Value</dd>
</div>
}
</dl>
</div>
</div>
<div class="card">
<div class="card-header">
<h2 class="card-title">@Localizer["Properties"]</h2>
</div>
<div class="card-content">
<dl class="grid grid-cols-1 gap-4">
@foreach (var prop in Model.AuthenticateResult.Properties.Items)
{
<div class="border-b pb-2">
<dt class="font-semibold text-sm mb-1">@prop.Key</dt>
<dd class="text-sm text-muted-foreground">@prop.Value</dd>
</div>
}
</dl>
</div>
</div>
@if (Model.Clients.Any())
{
<div class="card">
<div class="card-header">
<h2 class="card-title">@Localizer["Clients"]</h2>
</div>
<div class="card-content">
<ul class="list-disc list-inside space-y-2">
@foreach (var client in Model.Clients)
{
<li>@client</li>
}
</ul>
</div>
</div>
}
</div>
</div>
@@ -0,0 +1,107 @@
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
@model LiteCharmsSecurity.STS.Identity.ViewModels.Grants.GrantsViewModel
<section class="space-y-6">
<div class="card">
<div class="card-header">
<h2 class="card-title">@Localizer["Title"]</h2>
<p class="card-description">@Localizer["SubTitle"]</p>
</div>
<div class="card-content">
@if (!Model.Grants.Any())
{
<div class="alert-default">
<lucideicon name="info" class="h-4 w-4" />
<div>
<h5 class="alert-title">@Localizer["Info"]</h5>
<div class="alert-description">@Localizer["NoGrants"]</div>
</div>
</div>
}
else
{
<div class="space-y-4">
@foreach (var grant in Model.Grants)
{
<div class="rounded-lg border bg-card p-4 shadow-sm hover:shadow-md transition-shadow">
<div class="flex items-start gap-4">
<div class="flex h-14 w-14 shrink-0 items-center justify-center rounded-lg border bg-background">
@if (grant.ClientLogoUrl != null)
{
<img src="@grant.ClientLogoUrl" alt="@grant.ClientName" class="h-12 w-12 object-contain" />
}
else
{
<lucideicon name="app-window" class="h-6 w-6 text-muted-foreground" />
}
</div>
<div class="min-w-0 flex-1">
<h3 class="font-semibold text-base mb-2">@grant.ClientName</h3>
<div class="flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-muted-foreground mb-3">
<span class="inline-flex items-center gap-1">
<lucideicon name="calendar" class="h-3 w-3" />
<span class="font-medium">@Localizer["Created"]</span> @grant.Created.ToString("dd MMM yyyy")
</span>
@if (grant.Expires.HasValue)
{
<span class="inline-flex items-center gap-1">
<lucideicon name="clock" class="h-3 w-3" />
<span class="font-medium">@Localizer["Expires"]</span> @grant.Expires.Value.ToString("dd MMM yyyy")
</span>
}
</div>
@if (grant.IdentityGrantNames.Any())
{
<div class="mb-2">
<div class="text-xs font-semibold text-muted-foreground mb-1">
<lucideicon name="user" class="inline-block h-3 w-3 mr-1" />
@Localizer["IdentityGrants"]
</div>
<div class="flex flex-wrap gap-1">
@foreach (var name in grant.IdentityGrantNames)
{
<span class="badge-secondary text-xs">@name</span>
}
</div>
</div>
}
@if (grant.ApiGrantNames.Any())
{
<div>
<div class="text-xs font-semibold text-muted-foreground mb-1">
<lucideicon name="key" class="inline-block h-3 w-3 mr-1" />
@Localizer["ApiGrants"]
</div>
<div class="flex flex-wrap gap-1">
@foreach (var name in grant.ApiGrantNames)
{
<span class="badge-secondary text-xs">@name</span>
}
</div>
</div>
}
</div>
<div class="shrink-0">
<form asp-action="Revoke" method="post">
<input type="hidden" name="clientId" value="@grant.ClientId" />
<button class="btn-destructive btn-sm inline-flex items-center gap-1.5" title="@Localizer["RevokeAccess"]">
<lucideicon name="x-circle" class="h-4 w-4" />
<span>@Localizer["RevokeAccess"]</span>
</button>
</form>
</div>
</div>
</div>
}
</div>
}
</div>
</div>
</section>
@@ -0,0 +1,113 @@
@using Duende.IdentityServer.Extensions
@using Microsoft.AspNetCore.Mvc.Localization
@using LiteCharmsSecurity.STS.Identity.Configuration.Interfaces
@inject IViewLocalizer Localizer
@inject IRootConfiguration RootConfiguration
<div class="py-4">
<div class="text-center mb-6">
<img class="mx-auto h-20 w-auto mb-4 opacity-90 dark:invert" alt="Logo"
src="@UrlHelper.Content(RootConfiguration.AdminConfiguration.HomePageLogoUri)">
<h1 class="text-5xl font-bold mb-2 tracking-tight">@RootConfiguration.AdminConfiguration.PageTitle</h1>
<p class="text-lg text-muted-foreground mt-3">
@Localizer["SubTitle", RootConfiguration.AdminConfiguration.PageTitle]
</p>
</div>
@if (!User.IsAuthenticated())
{
<section class="flex min-h-[40vh] items-center justify-center py-12">
<div class="flex flex-wrap justify-center gap-6 max-w-5xl">
<a asp-controller="Account" asp-action="Login"
class="card w-80 hover:border-primary/40 transition-all hover:shadow-xl duration-300 group block">
<div class="card-content text-center py-10 px-6">
<div
class="mx-auto mb-6 inline-flex h-24 w-24 items-center justify-center rounded-2xl bg-gradient-to-br from-primary/15 to-primary/5 group-hover:from-primary/25 group-hover:to-primary/10 transition-all duration-300">
<lucideicon name="log-in" class="h-12 w-12 text-primary" />
</div>
<h2 class="text-2xl font-bold mb-3">@Localizer["Login"]</h2>
<p class="text-sm text-muted-foreground mb-8">@Localizer["LoginDescription"]</p>
</div>
</a>
@if (RootConfiguration.RegisterConfiguration.Enabled)
{
<a asp-controller="Account" asp-action="Register"
class="card w-80 hover:border-primary/40 transition-all hover:shadow-xl duration-300 group block">
<div class="card-content text-center py-10 px-6">
<div
class="mx-auto mb-6 inline-flex h-24 w-24 items-center justify-center rounded-2xl bg-gradient-to-br from-primary/15 to-primary/5 group-hover:from-primary/25 group-hover:to-primary/10 transition-all duration-300">
<lucideicon name="user-plus" class="h-12 w-12 text-primary" />
</div>
<h2 class="text-2xl font-bold mb-3">@Localizer["Register"]</h2>
<p class="text-sm text-muted-foreground mb-8">@Localizer["RegisterDescription"]</p>
</div>
</a>
}
<a href="~/.well-known/openid-configuration"
class="card w-80 hover:border-primary/40 transition-all hover:shadow-xl duration-300 group block">
<div class="card-content text-center py-10 px-6">
<div
class="mx-auto mb-6 inline-flex h-24 w-24 items-center justify-center rounded-2xl bg-gradient-to-br from-primary/15 to-primary/5 group-hover:from-primary/25 group-hover:to-primary/10 transition-all duration-300">
<lucideicon name="globe" class="h-12 w-12 text-primary" />
</div>
<h2 class="text-2xl font-bold mb-3">@Localizer["Discovery"]</h2>
<p class="text-sm text-muted-foreground mb-8">@Localizer["DiscoveryDescription"]</p>
</div>
</a>
</div>
</section>
}
else
{
<div class="grid gap-8 md:grid-cols-2 lg:grid-cols-3 mb-10">
@{
var menuItems = new[] {
new { Title = Localizer["Grants"], Icon = "badge-check", Controller = "Grants", Action = "Index", Description =
Localizer["GrantsDescription"], Url = "" },
new { Title = Localizer["Discovery"], Icon = "globe", Controller = "", Action = "", Description =
Localizer["DiscoveryDescription"], Url = Url.Content("~/.well-known/openid-configuration") },
new { Title = Localizer["ManageProfile"], Icon = "user", Controller = "Manage", Action = "Index", Description =
Localizer["ManageProfileDescription"], Url = "" },
new { Title = Localizer["ManagePersonalData"], Icon = "cloud-download", Controller = "Manage", Action =
"PersonalData", Description = Localizer["ManagePersonalDataDescription"], Url = "" },
new { Title = Localizer["2FA"], Icon = "shield-check", Controller = "Manage", Action = "TwoFactorAuthentication",
Description = Localizer["2FADescription"], Url = "" },
new { Title = Localizer["ChangePassword"], Icon = "key-round", Controller = "Manage", Action = "ChangePassword",
Description = Localizer["ChangePasswordDescription"], Url = "" }
};
}
@foreach (var item in menuItems)
{
@if (!string.IsNullOrEmpty(item.Url))
{
<a href="@item.Url" class="card hover:border-primary/40 transition-all hover:shadow-xl duration-300 group block">
<div class="card-content text-center py-10 px-6">
<div
class="mx-auto mb-6 inline-flex h-24 w-24 items-center justify-center rounded-2xl bg-gradient-to-br from-primary/15 to-primary/5 group-hover:from-primary/25 group-hover:to-primary/10 transition-all duration-300">
<lucideicon name="@item.Icon" class="h-12 w-12 text-primary" />
</div>
<h2 class="text-2xl font-bold mb-3">@item.Title</h2>
<p class="text-sm text-muted-foreground mb-8">@item.Description</p>
</div>
</a>
}
else
{
<a asp-controller="@item.Controller" asp-action="@item.Action"
class="card hover:border-primary/40 transition-all hover:shadow-xl duration-300 group block">
<div class="card-content text-center py-10 px-6">
<div
class="mx-auto mb-6 inline-flex h-24 w-24 items-center justify-center rounded-2xl bg-gradient-to-br from-primary/15 to-primary/5 group-hover:from-primary/25 group-hover:to-primary/10 transition-all duration-300">
<lucideicon name="@item.Icon" class="h-12 w-12 text-primary" />
</div>
<h2 class="text-2xl font-bold mb-3">@item.Title</h2>
<p class="text-sm text-muted-foreground mb-8">@item.Description</p>
</div>
</a>
}
}
</div>
}
</div>
@@ -0,0 +1,55 @@
@inject IViewLocalizer Localizer
@using Microsoft.AspNetCore.Mvc.Localization
@model LiteCharmsSecurity.STS.Identity.ViewModels.Manage.ChangePasswordViewModel
@{
ViewData["Title"] = Localizer["Title"];
}
<section class="space-y-6">
<div class="card">
<div class="card-header">
<h2 class="card-title">@ViewData["Title"]</h2>
</div>
<div class="card-content space-y-6">
@await Html.PartialAsync("_ValidationSummary")
@await Html.PartialAsync("_StatusMessage", Model.StatusMessage)
<form method="post" class="space-y-4">
<div class="form-item">
<label asp-for="OldPassword" class="form-label">
<lucideicon name="lock" class="inline-block h-4 w-4 mr-1" />
@Localizer["OldPassword"]
</label>
<input asp-for="OldPassword" type="password" class="input" tw-validation />
<span asp-validation-for="OldPassword" class="form-message"></span>
</div>
<div class="form-item">
<label asp-for="NewPassword" class="form-label">
<lucideicon name="key-round" class="inline-block h-4 w-4 mr-1" />
@Localizer["NewPassword"]
</label>
<input asp-for="NewPassword" type="password" class="input" tw-validation />
<span asp-validation-for="NewPassword" class="form-message"></span>
</div>
<div class="form-item">
<label asp-for="ConfirmPassword" class="form-label">
<lucideicon name="lock-keyhole" class="inline-block h-4 w-4 mr-1" />
@Localizer["ConfirmPassword"]
</label>
<input asp-for="ConfirmPassword" type="password" class="input" tw-validation />
<span asp-validation-for="ConfirmPassword" class="form-message"></span>
</div>
<div>
<button type="submit" class="btn-default">
<lucideicon name="save" class="inline-block h-4 w-4 mr-2" />
@Localizer["Update"]
</button>
</div>
</form>
</div>
</div>
</section>
@@ -0,0 +1,43 @@
@inject IViewLocalizer Localizer
@using Microsoft.AspNetCore.Mvc.Localization
@model LiteCharmsSecurity.STS.Identity.ViewModels.Manage.DeletePersonalDataViewModel
@{
ViewData["Title"] = Localizer["Title"];
}
<section class="space-y-6 max-w-2xl">
<div class="card">
<div class="card-header">
<h2 class="card-title">@ViewData["Title"]</h2>
</div>
<div class="card-content space-y-6">
<div class="alert-destructive">
<lucideicon name="alert-circle" class="h-4 w-4" />
<div>
<div class="alert-title">@Localizer["Alert"]</div>
</div>
</div>
<form id="delete-user" method="post" class="space-y-4">
@await Html.PartialAsync("_ValidationSummary")
@if (Model.RequirePassword)
{
<div class="form-item">
<label asp-for="Password" class="form-label">
<lucideicon name="lock" class="inline-block h-4 w-4 mr-1" />
@Localizer["Password"]
</label>
<input asp-for="Password" type="password" class="input" tw-validation />
<span asp-validation-for="Password" class="form-message"></span>
</div>
}
<button class="btn-destructive" type="submit">
<lucideicon name="trash-2" class="inline-block h-4 w-4 mr-2" />
@Localizer["Delete"]
</button>
</form>
</div>
</div>
</section>
@@ -0,0 +1,30 @@
@inject IViewLocalizer Localizer
@using Microsoft.AspNetCore.Mvc.Localization
@{
ViewData["Title"] = Localizer["Title"];
}
<section class="space-y-6 max-w-2xl">
<div class="card">
<div class="card-header">
<h2 class="card-title">@ViewData["Title"]</h2>
</div>
<div class="card-content space-y-6">
<div class="alert-destructive">
<lucideicon name="alert-circle" class="h-4 w-4" />
<div>
<div class="alert-title">@Localizer["SubTitle"]</div>
<div class="alert-description">@Localizer["Info"] <a asp-action="ResetAuthenticatorWarning" class="underline underline-offset-4">@Localizer["ResetAuthenticator"]</a></div>
</div>
</div>
<form asp-action="Disable2fa" method="post">
<button class="btn-destructive" type="submit">
<lucideicon name="shield-off" class="inline-block h-4 w-4 mr-2" />
@Localizer["Disable2FA"]
</button>
</form>
</div>
</div>
</section>
@@ -0,0 +1,10 @@
@inject IViewLocalizer Localizer
@using Microsoft.AspNetCore.Mvc.Localization
@{
ViewData["Title"] = Localizer["Title"];
}
<div class="py-8">
<h1 class="text-4xl font-bold">@ViewData["Title"]</h1>
</div>
@@ -0,0 +1,59 @@
@inject IViewLocalizer Localizer
@using Microsoft.AspNetCore.Mvc.Localization
@model LiteCharmsSecurity.STS.Identity.ViewModels.Manage.EnableAuthenticatorViewModel
@{
ViewData["Title"] = Localizer["Title"];
}
<section class="space-y-6">
<div class="card">
<div class="card-header">
<h2 class="card-title">@ViewData["Title"]</h2>
<p class="card-description">@Localizer["SubTitle"]</p>
</div>
<div class="card-content space-y-6">
<ol class="space-y-6 list-decimal pl-5 text-sm text-muted-foreground">
<li class="space-y-2">
<p class="text-foreground">
@Localizer["DownloadTitle"]
<a href="https://go.microsoft.com/fwlink/?Linkid=825071" class="text-primary hover:underline">Windows Phone</a>,
<a href="https://go.microsoft.com/fwlink/?Linkid=825072" class="text-primary hover:underline">Android</a>,
<a href="https://go.microsoft.com/fwlink/?Linkid=825073" class="text-primary hover:underline">iOS</a>.
@Localizer["GoogleAuthenticatorTitle"]
<a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&amp;hl=en" class="text-primary hover:underline">Android</a>,
<a href="https://itunes.apple.com/us/app/google-authenticator/id388497605?mt=8" class="text-primary hover:underline">iOS</a>.
</p>
</li>
<li class="space-y-3">
<p class="text-foreground">@Localizer["ScanQR"] <kbd class="rounded bg-muted px-1 py-0.5 text-xs">@Model.SharedKey</kbd> @Localizer["ScanQRSubTitle"]</p>
<div id="qrCode" class="rounded-md border p-4 inline-block"></div>
<div id="qrCodeData" data-url="@Model.AuthenticatorUri"></div>
</li>
<li class="space-y-3">
<p class="text-foreground">@Localizer["Info"]</p>
<form method="post" class="space-y-4 max-w-md">
@await Html.PartialAsync("_ValidationSummary")
<div class="form-item">
<label asp-for="Code" class="form-label">
<lucideicon name="shield-check" class="inline-block h-4 w-4 mr-1" />
@Localizer["Code"]
</label>
<input asp-for="Code" class="input" autocomplete="off" tw-validation />
<span asp-validation-for="Code" class="form-message"></span>
</div>
<button type="submit" class="btn-default">
<lucideicon name="check" class="inline-block h-4 w-4 mr-2" />
@Localizer["Verify"]
</button>
</form>
</li>
</ol>
</div>
</div>
</section>
<script src="~/js/qrcode.min.js"></script>
<script src="~/js/enable-authenticator.js" asp-append-version="true"></script>
@@ -0,0 +1,69 @@
@inject IViewLocalizer Localizer
@using Microsoft.AspNetCore.Mvc.Localization
@model LiteCharmsSecurity.STS.Identity.ViewModels.Manage.ExternalLoginsViewModel
@{
ViewData["Title"] = Localizer["Title"];
}
<section class="space-y-6">
@await Html.PartialAsync("_StatusMessage", Model.StatusMessage)
<div class="card">
<div class="card-header">
<h2 class="card-title">@Localizer["SubTitle"]</h2>
</div>
<div class="card-content space-y-6">
@if (Model.CurrentLogins?.Count > 0)
{
<div class="table-wrapper">
<table class="table">
<tbody class="table-body">
@foreach (var login in Model.CurrentLogins)
{
<tr class="table-row">
<td class="table-cell font-medium">@login.LoginProvider</td>
<td class="table-cell text-right">
@if (Model.ShowRemoveButton)
{
<form asp-action="RemoveLogin" method="post" class="inline">
<input asp-for="@login.LoginProvider" name="LoginProvider" type="hidden" />
<input asp-for="@login.ProviderKey" name="ProviderKey" type="hidden" />
<button type="submit" class="btn-destructive btn-sm" title="Remove this @login.LoginProvider login from your account">
<lucideicon name="unlink" class="inline-block h-3 w-3 mr-1" />
@Localizer["Remove"]
</button>
</form>
}
</td>
</tr>
}
</tbody>
</table>
</div>
}
else
{
<div class="alert-default">
<lucideicon name="info" class="h-4 w-4" />
<div>
<div class="alert-description">@Localizer["Title"]</div>
</div>
</div>
}
@if (Model.OtherLogins?.Count > 0)
{
<div class="space-y-3">
<h4 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">@Localizer["TitleAddAnotherService"]</h4>
<form asp-action="LinkLogin" method="post" class="flex flex-wrap gap-3">
@foreach (var provider in Model.OtherLogins)
{
<button type="submit" class="btn-outline" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button>
}
</form>
</div>
}
</div>
</div>
</section>
@@ -0,0 +1,33 @@
@inject IViewLocalizer Localizer
@using Microsoft.AspNetCore.Mvc.Localization
@{
ViewData["Title"] = Localizer["Title"];
}
<section class="space-y-6 max-w-2xl">
<div class="card">
<div class="card-header">
<h2 class="card-title">@ViewData["Title"]</h2>
</div>
<div class="card-content space-y-6">
<div class="alert-default">
<lucideicon name="info" class="h-4 w-4" />
<div>
<div class="alert-title">@Localizer["SubTitle"]</div>
<div class="alert-description space-y-2">
<p>@Localizer["LostInfo"]</p>
<p>@Localizer["GenerateRecoveryCodeInfo"] <a asp-action="ResetAuthenticatorWarning" class="underline underline-offset-4">@Localizer["ResetKeys"]</a></p>
</div>
</div>
</div>
<form asp-action="GenerateRecoveryCodes" method="post">
<button class="btn-destructive" type="submit">
<lucideicon name="refresh-cw" class="inline-block h-4 w-4 mr-2" />
@Localizer["GenerateCodes"]
</button>
</form>
</div>
</div>
</section>
@@ -0,0 +1,145 @@
@inject IViewLocalizer Localizer
@using Microsoft.AspNetCore.Mvc.Localization
@model LiteCharmsSecurity.STS.Identity.ViewModels.Manage.IndexViewModel
<section class="space-y-6">
<div class="card">
<div class="card-header">
<h2 class="card-title">@Localizer["Title"]</h2>
<p class="card-description">@Localizer["Manage your profile and contact information."]</p>
</div>
<div class="card-content space-y-6">
@await Html.PartialAsync("_ValidationSummary")
@await Html.PartialAsync("_StatusMessage", Model.StatusMessage)
<div class="grid gap-6 md:grid-cols-[180px,1fr]">
<div class="flex items-start">
<img-gravatar email="@Model.Email" class="rounded-full border shadow-sm" size="150" />
</div>
<form method="post" class="space-y-6">
<!-- Username (read-only) -->
<div class="form-item">
<label asp-for="Username" class="form-label" tw-validation>
<lucideicon name="user" class="inline-block h-4 w-4 mr-1" />
@Localizer["Username"]
</label>
<input asp-for="Username" class="input" disabled tw-validation />
<span asp-validation-for="Username" class="form-message"></span>
</div>
<!-- Email + verification -->
<div class="form-item">
<label asp-for="Email" class="form-label" tw-validation>
<lucideicon name="mail" class="inline-block h-4 w-4 mr-1" />
@Localizer["Email"]
</label>
<input type="hidden" asp-for="IsEmailConfirmed" />
<div class="flex flex-col gap-2 sm:flex-row sm:items-center">
<input asp-for="Email" class="input sm:flex-1" tw-validation />
@if (!Model.IsEmailConfirmed)
{
<button asp-action="SendVerificationEmail" class="btn-link">@Localizer["SendVerificationEmail"]</button>
}
</div>
<span asp-validation-for="Email" class="form-message"></span>
</div>
<!-- Contact -->
<div class="grid gap-6 md:grid-cols-2">
<div class="form-item">
<label asp-for="PhoneNumber" class="form-label" tw-validation>
<lucideicon name="phone" class="inline-block h-4 w-4 mr-1" />
@Localizer["Phone number"]
</label>
<input asp-for="PhoneNumber" class="input" tw-validation />
<span asp-validation-for="PhoneNumber" class="form-message"></span>
</div>
<div class="form-item">
<label asp-for="Name" class="form-label" tw-validation>
<lucideicon name="user-circle" class="inline-block h-4 w-4 mr-1" />
@Localizer["Full name"]
</label>
<input asp-for="Name" class="input" tw-validation />
<span asp-validation-for="Name" class="form-message"></span>
</div>
</div>
<div class="form-item">
<label asp-for="Website" class="form-label" tw-validation>
<lucideicon name="globe" class="inline-block h-4 w-4 mr-1" />
@Localizer["Website"]
</label>
<input asp-for="Website" class="input" tw-validation />
<span asp-validation-for="Website" class="form-message"></span>
</div>
<div class="form-item">
<label asp-for="Profile" class="form-label" tw-validation>
<lucideicon name="link" class="inline-block h-4 w-4 mr-1" />
@Localizer["Profile"]
</label>
<input asp-for="Profile" class="input" tw-validation />
<span asp-validation-for="Profile" class="form-message"></span>
</div>
<!-- Address -->
<div class="form-item">
<label asp-for="StreetAddress" class="form-label" tw-validation>
<lucideicon name="map-pin" class="inline-block h-4 w-4 mr-1" />
@Localizer["Street address"]
</label>
<input asp-for="StreetAddress" class="input" tw-validation />
<span asp-validation-for="StreetAddress" class="form-message"></span>
</div>
<div class="grid gap-6 md:grid-cols-2">
<div class="form-item">
<label asp-for="Locality" class="form-label" tw-validation>
<lucideicon name="building" class="inline-block h-4 w-4 mr-1" />
@Localizer["City"]
</label>
<input asp-for="Locality" class="input" tw-validation />
<span asp-validation-for="Locality" class="form-message"></span>
</div>
<div class="form-item">
<label asp-for="Region" class="form-label" tw-validation>
<lucideicon name="map" class="inline-block h-4 w-4 mr-1" />
@Localizer["State/Province"]
</label>
<input asp-for="Region" class="input" tw-validation />
<span asp-validation-for="Region" class="form-message"></span>
</div>
</div>
<div class="grid gap-6 md:grid-cols-2">
<div class="form-item">
<label asp-for="PostalCode" class="form-label" tw-validation>
<lucideicon name="hash" class="inline-block h-4 w-4 mr-1" />
@Localizer["Postal code"]
</label>
<input asp-for="PostalCode" class="input" tw-validation />
<span asp-validation-for="PostalCode" class="form-message"></span>
</div>
<div class="form-item">
<label asp-for="Country" class="form-label" tw-validation>
<lucideicon name="flag" class="inline-block h-4 w-4 mr-1" />
@Localizer["Country"]
</label>
<input asp-for="Country" class="input" tw-validation />
<span asp-validation-for="Country" class="form-message"></span>
</div>
</div>
<div class="flex gap-3">
<button type="submit" class="btn-default">
<lucideicon name="save" class="inline-block h-4 w-4 mr-2" />
@Localizer["Save"]
</button>
</div>
</form>
</div>
</div>
</div>
</section>
@@ -0,0 +1,17 @@
@inject IViewLocalizer Localizer
@using Microsoft.AspNetCore.Mvc.Localization
@{
ViewData["Title"] = Localizer["Title"];
}
<div class="py-8">
@await Html.PartialAsync("_ValidationSummary")
<h1 class="text-4xl font-bold mb-2">@ViewData["Title"]</h1>
<div class="alert-destructive">
<lucideicon name="alert-circle" class="h-4 w-4" />
<div>
<div class="alert-description">@Localizer["Error"]</div>
</div>
</div>
</div>
@@ -0,0 +1,70 @@
@using System.Buffers.Text
@using LiteCharmsSecurity.STS.Identity.Passkeys
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
@model LiteCharmsSecurity.STS.Identity.ViewModels.Manage.PasskeysViewModel
@{
ViewData["Title"] = Localizer["Title"];
}
<partial name="_StatusMessage" model="@Model.StatusMessage" />
<h3 class="text-2xl font-bold mb-6">@ViewData["Title"]</h3>
<div class="card mb-6">
<div class="card-content py-6">
@if (Model.CurrentPasskeys?.Count > 0)
{
<div class="space-y-4">
@foreach (var passkey in Model.CurrentPasskeys)
{
var credentialId = Base64Url.EncodeToString(passkey.CredentialId);
<div class="flex items-center justify-between py-3 border-b border-border last:border-0">
<div class="flex items-center gap-3">
<lucideicon name="fingerprint" class="h-5 w-5 text-muted-foreground" />
<span>@(passkey.Name ?? Localizer["UnnamedPasskey"].Value)</span>
</div>
<form asp-action="UpdatePasskey" method="post" class="flex items-center gap-2">
<input type="hidden" name="CredentialId" value="@credentialId" />
<button type="submit" name="Action" value="rename" class="btn-outline btn-sm">
<lucideicon name="pencil" class="h-4 w-4 mr-1" />
@Localizer["Rename"]
</button>
<button type="submit" name="Action" value="delete" class="btn-destructive btn-sm"
onclick="return confirm('@Localizer["ConfirmDelete"]');">
<lucideicon name="trash-2" class="h-4 w-4 mr-1" />
@Localizer["Delete"]
</button>
</form>
</div>
}
</div>
}
else
{
<div class="text-center py-8 text-muted-foreground">
<lucideicon name="fingerprint" class="h-12 w-12 mx-auto mb-4 opacity-50" />
<p>@Localizer["NoPasskeys"]</p>
</div>
}
</div>
</div>
<div class="card">
<div class="card-header">
<h4 class="card-title">@Localizer["AddPasskey"]</h4>
<p class="card-description">@Localizer["AddPasskeyDescription"]</p>
</div>
<div class="card-content">
<form asp-action="AddPasskey" method="post">
<passkey-submit operation="@PasskeyOperation.Create" name="Passkey" class="btn-default" formnovalidate>
<lucideicon name="plus" class="inline-block h-4 w-4 mr-2" />
@Localizer["AddPasskeyButton"]
</passkey-submit>
</form>
</div>
</div>
@section scripts {
<script src="~/js/passkey-submit.js" asp-append-version="true"></script>
}
@@ -0,0 +1,40 @@
@inject IViewLocalizer Localizer
@using Microsoft.AspNetCore.Mvc.Localization
@{
ViewData["Title"] = Localizer["Title"];
}
<div class="py-8">
<h1 class="text-4xl font-bold mb-6">@ViewData["Title"]</h1>
<div class="max-w-2xl space-y-6">
<div class="alert-default">
<lucideicon name="info" class="h-4 w-4" />
<div>
<div class="alert-description">@Localizer["Info"]</div>
</div>
</div>
<form asp-action="DownloadPersonalData" method="post">
<button class="btn-default" type="submit">
<lucideicon name="download" class="inline-block h-4 w-4 mr-2" />
@Localizer["Download"]
</button>
</form>
<div class="alert-destructive">
<lucideicon name="alert-circle" class="h-4 w-4" />
<div>
<div class="alert-description">@Localizer["Warning"]</div>
</div>
</div>
<div>
<a asp-action="DeletePersonalData" class="btn-destructive">
<lucideicon name="trash-2" class="inline-block h-4 w-4 mr-2" />
@Localizer["Delete"]
</a>
</div>
</div>
</div>
@@ -0,0 +1,47 @@
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
@model LiteCharmsSecurity.STS.Identity.ViewModels.Manage.RenamePasskeyViewModel
@{
ViewData["Title"] = Localizer["Title"];
}
<partial name="_StatusMessage" model="@Model.StatusMessage" />
<h3 class="text-2xl font-bold mb-6">@ViewData["Title"]</h3>
<div class="card">
<div class="card-content py-6">
@if (!string.IsNullOrEmpty(Model.Name))
{
<p class="text-muted-foreground mb-4">@Localizer["RenameDescription", Model.Name]</p>
}
else
{
<p class="text-muted-foreground mb-4">@Localizer["NameDescription"]</p>
}
<form asp-action="RenamePasskey" method="post" class="space-y-4">
<input type="hidden" asp-for="CredentialId" />
<div class="form-item">
<label class="form-label" for="Name">
<lucideicon name="tag" class="inline-block h-4 w-4 mr-1" />
@Localizer["Name"]
</label>
<input id="Name" class="input" asp-for="Name" autofocus autocomplete="off" />
<span asp-validation-for="Name" class="text-destructive text-sm"></span>
</div>
<div class="flex gap-2">
<button type="submit" class="btn-default">
<lucideicon name="check" class="inline-block h-4 w-4 mr-2" />
@Localizer["Save"]
</button>
<a asp-action="Passkeys" class="btn-outline">
<lucideicon name="x" class="inline-block h-4 w-4 mr-2" />
@Localizer["Cancel"]
</a>
</div>
</form>
</div>
</div>
@@ -0,0 +1,30 @@
@inject IViewLocalizer Localizer
@using Microsoft.AspNetCore.Mvc.Localization
@{
ViewData["Title"] = Localizer["Title"];
}
<section class="space-y-6 max-w-2xl">
<div class="card">
<div class="card-header">
<h2 class="card-title">@ViewData["Title"]</h2>
</div>
<div class="card-content space-y-6">
<div class="alert-destructive">
<lucideicon name="alert-circle" class="h-4 w-4" />
<div>
<div class="alert-title">@Localizer["SubTitle"]</div>
<div class="alert-description">@Localizer["Info"]</div>
</div>
</div>
<form asp-action="ResetAuthenticator" method="post">
<button class="btn-destructive" type="submit">
<lucideicon name="rotate-ccw" class="inline-block h-4 w-4 mr-2" />
@Localizer["Reset"]
</button>
</form>
</div>
</div>
</section>
@@ -0,0 +1,49 @@
@inject IViewLocalizer Localizer
@using Microsoft.AspNetCore.Mvc.Localization
@model LiteCharmsSecurity.STS.Identity.ViewModels.Manage.SetPasswordViewModel
@{
ViewData["Title"] = Localizer["Title"];
}
<section class="space-y-6">
<div class="card max-w-2xl">
<div class="card-header">
<h2 class="card-title">@Localizer["SubTitle"]</h2>
</div>
<div class="card-content space-y-6">
@await Html.PartialAsync("_StatusMessage", Model.StatusMessage)
<div class="alert-default">
<lucideicon name="info" class="h-4 w-4" />
<div>
<div class="alert-description">@Localizer["Info"]</div>
</div>
</div>
<form method="post" class="space-y-4">
<div class="form-item">
<label asp-for="NewPassword" class="form-label">
<lucideicon name="key-round" class="inline-block h-4 w-4 mr-1" />
@Localizer["NewPassword"]
</label>
<input asp-for="NewPassword" type="password" class="input" tw-validation />
<span asp-validation-for="NewPassword" class="form-message"></span>
</div>
<div class="form-item">
<label asp-for="ConfirmPassword" class="form-label">
<lucideicon name="lock-keyhole" class="inline-block h-4 w-4 mr-1" />
@Localizer["ConfirmPassword"]
</label>
<input asp-for="ConfirmPassword" type="password" class="input" tw-validation />
<span asp-validation-for="ConfirmPassword" class="form-message"></span>
</div>
<div>
<button type="submit" class="btn-default">
<lucideicon name="save" class="inline-block h-4 w-4 mr-2" />
@Localizer["SetPassword"]
</button>
</div>
</form>
</div>
</div>
</section>
@@ -0,0 +1,34 @@
@inject IViewLocalizer Localizer
@using Microsoft.AspNetCore.Mvc.Localization
@model LiteCharmsSecurity.STS.Identity.ViewModels.Manage.ShowRecoveryCodesViewModel
@{
ViewData["Title"] = Localizer["Title"];
}
<section class="space-y-6 max-w-2xl">
<div class="card">
<div class="card-header">
<h2 class="card-title">@ViewData["Title"]</h2>
</div>
<div class="card-content space-y-6">
<div class="alert-default">
<lucideicon name="info" class="h-4 w-4" />
<div>
<div class="alert-title">@Localizer["SubTitle"]</div>
<div class="alert-description">@Localizer["Info"]</div>
</div>
</div>
<div class="grid gap-3 sm:grid-cols-2 font-mono text-sm">
@for (var row = 0; row < Model.RecoveryCodes.Length; row += 2)
{
<div class="flex items-center justify-between rounded-md border bg-muted/40 px-3 py-2">
<span>@Model.RecoveryCodes[row]</span>
<span>@Model.RecoveryCodes[row + 1]</span>
</div>
}
</div>
</div>
</div>
</section>
@@ -0,0 +1,95 @@
@inject IViewLocalizer Localizer
@using Microsoft.AspNetCore.Mvc.Localization
@model LiteCharmsSecurity.STS.Identity.ViewModels.Manage.TwoFactorAuthenticationViewModel
@{
ViewData["Title"] = Localizer["Title"];
}
<section class="space-y-6">
@if (Model.Is2faEnabled)
{
<div class="card">
<div class="card-header">
<h2 class="card-title">@ViewData["Title"]</h2>
</div>
<div class="card-content space-y-4">
@if (Model.RecoveryCodesLeft == 0)
{
<div class="alert-destructive">
<lucideicon name="alert-circle" class="h-4 w-4" />
<div>
<div class="alert-title">@Localizer["NoCodes"]</div>
<div class="alert-description">@Localizer["YouMust"] <a asp-action="GenerateRecoveryCodes" class="underline underline-offset-4">@Localizer["GenerateNewCodes"]</a> @Localizer["BeforeLogin"]</div>
</div>
</div>
}
else if (Model.RecoveryCodesLeft == 1)
{
<div class="alert-destructive">
<lucideicon name="alert-circle" class="h-4 w-4" />
<div>
<div class="alert-title">@Localizer["OneCode"]</div>
<div class="alert-description">@Localizer["YouCanGenerateCodes"] <a asp-action="GenerateRecoveryCodes" class="underline underline-offset-4">@Localizer["GenerateNewCodes"]</a></div>
</div>
</div>
}
else if (Model.RecoveryCodesLeft <= 3)
{
<div class="alert-default">
<lucideicon name="info" class="h-4 w-4" />
<div>
<div class="alert-title">@Localizer["YouHave"] @Model.RecoveryCodesLeft.ToString() @Localizer["RecoveryCodeLeft"]</div>
<div class="alert-description">@Localizer["YouShould"] <a asp-action="GenerateRecoveryCodes" class="underline underline-offset-4">@Localizer["GenerateNewCodes"]</a></div>
</div>
</div>
}
@if (Model.IsMachineRemembered)
{
<form method="post" asp-controller="Manage" asp-action="ForgetTwoFactorClient">
<button type="submit" class="btn-outline">@Localizer["ForgetBrowser"]</button>
</form>
}
<div class="flex flex-wrap gap-3">
<a asp-action="Disable2faWarning" class="btn-outline">
<lucideicon name="shield-off" class="inline-block h-4 w-4 mr-2" />
@Localizer["Disable2FA"]
</a>
<a asp-action="GenerateRecoveryCodesWarning" class="btn-destructive">
<lucideicon name="refresh-cw" class="inline-block h-4 w-4 mr-2" />
@Localizer["ResetCodes"]
</a>
</div>
</div>
</div>
}
<div class="card">
<div class="card-header">
<h3 class="card-title">@Localizer["AuthenticatorApp"]</h3>
</div>
<div class="card-content">
@if (!Model.HasAuthenticator)
{
<a id="enable-authenticator" asp-action="EnableAuthenticator" class="btn-default">
<lucideicon name="smartphone" class="inline-block h-4 w-4 mr-2" />
@Localizer["AddAuthenticator"]
</a>
}
else
{
<div class="flex flex-wrap gap-3">
<a id="enable-authenticator" asp-action="EnableAuthenticator" class="btn-default">
<lucideicon name="settings" class="inline-block h-4 w-4 mr-2" />
@Localizer["SetupAuthenticator"]
</a>
<a id="reset-authenticator" asp-action="ResetAuthenticatorWarning" class="btn-destructive">
<lucideicon name="rotate-ccw" class="inline-block h-4 w-4 mr-2" />
@Localizer["ResetAuthenticator"]
</a>
</div>
}
</div>
</div>
</section>
@@ -0,0 +1,13 @@
@model string
@if (!String.IsNullOrEmpty(Model))
{
var statusMessageClass = Model.StartsWith("Error") ? "alert-destructive" : "alert-default";
var iconName = Model.StartsWith("Error") ? "alert-circle" : "check";
<div class="@statusMessageClass mb-4">
<lucideicon name="@iconName" class="h-4 w-4" />
<div>
<div class="alert-description">@Model</div>
</div>
</div>
}
@@ -0,0 +1,109 @@
@model LiteCharmsSecurity.STS.Identity.Models.ViewModels.ServerSideSessionsViewModel
@inject Microsoft.AspNetCore.Mvc.Localization.IViewLocalizer Localizer
@{
Layout = "_Layout";
}
<div class="space-y-6">
<div class="rounded-lg border border-border bg-background/60 p-4 shadow-sm">
<div class="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
<div>
<p class="text-lg font-semibold text-foreground">@Localizer["ServerSideSessionsTitle"]</p>
<p class="text-sm text-muted-foreground">
@Localizer["ServerSideSessionsDescription"]
</p>
</div>
<div class="flex flex-wrap items-center gap-2">
@if (Model.HasPrevious)
{
<a class="inline-flex h-9 items-center justify-center rounded border border-border px-3 text-xs font-semibold uppercase tracking-widest text-muted-foreground transition hover:border-foreground hover:text-foreground"
asp-action="Index"
asp-route-page="@(Model.Page - 1)"
asp-route-filter="@Model.Filter">
@Localizer["ServerSideSessionsPrev"]
</a>
}
<form method="get" class="flex flex-1 items-center gap-2">
<label class="text-xs font-semibold uppercase tracking-wide text-muted-foreground" for="Filter">@Localizer["ServerSideSessionsFilterLabel"]</label>
<input id="Filter"
name="Filter"
value="@Model.Filter"
class="flex-1 rounded border border-border px-3 py-1.5 text-sm placeholder:text-muted-foreground focus:border-accent focus:outline-none"
placeholder="@Localizer["ServerSideSessionsFilterPlaceholder"]" />
<input type="hidden" name="page" value="1" />
<button type="submit" class="inline-flex h-9 items-center justify-center rounded bg-accent px-3 text-xs font-semibold text-accent-foreground transition hover:bg-accent/90">
@Localizer["ServerSideSessionsSearch"]
</button>
</form>
@if (Model.HasNext)
{
<a class="inline-flex h-9 items-center justify-center rounded border border-border px-3 text-xs font-semibold uppercase tracking-widest text-muted-foreground transition hover:border-foreground hover:text-foreground"
asp-action="Index"
asp-route-page="@(Model.Page + 1)"
asp-route-filter="@Model.Filter">
@Localizer["ServerSideSessionsNext"]
</a>
}
</div>
</div>
@if (Model.TotalCount > 0)
{
<p class="mt-3 text-xs text-muted-foreground">
@if (Model.HasNext || Model.Page > 1)
{
@Localizer["ServerSideSessionsTotalResultsWithPage", Model.TotalCount, Model.Page, Model.TotalPages]
}
else
{
@Localizer["ServerSideSessionsTotalResults", Model.TotalCount]
}
</p>
}
</div>
<div class="rounded-lg border border-border bg-background/60 p-4 shadow-sm">
@if (Model.Sessions?.Any() == true)
{
<div class="overflow-x-auto">
<table class="w-full text-sm">
<thead>
<tr class="text-left text-xs uppercase tracking-widest text-muted-foreground">
<th class="pb-2">@Localizer["ServerSideSessionsDisplayName"]</th>
<th class="pb-2">@Localizer["ServerSideSessionsCreated"]</th>
<th class="pb-2">@Localizer["ServerSideSessionsExpires"]</th>
<th class="pb-2"></th>
</tr>
</thead>
<tbody class="divide-y divide-border">
@foreach (var session in Model.Sessions)
{
<tr>
<td>@session.DisplayName</td>
<td>@session.Created</td>
<td>@session.Expires</td>
<td>
<form asp-action="Delete" method="post" class="m-0">
@Html.AntiForgeryToken()
<input type="hidden" name="SessionId" value="@session.SessionId" />
<input type="hidden" name="Filter" value="@Model.Filter" />
<input type="hidden" name="page" value="@Model.Page" />
<input type="hidden" name="pageSize" value="@Model.PageSize" />
<button type="submit" class="inline-flex h-8 items-center justify-center rounded border border-transparent bg-red-50 px-3 text-xs font-semibold text-red-600 transition hover:bg-red-100 dark:bg-red-900/10 dark:text-red-400 dark:hover:bg-red-900/20">
@Localizer["ServerSideSessionsDelete"]
</button>
</form>
</td>
</tr>
}
</tbody>
</table>
</div>
}
else
{
<div class="flex items-center justify-center py-16 text-sm text-muted-foreground">
@Localizer["ServerSideSessionsNoResults"]
</div>
}
</div>
</div>
@@ -0,0 +1,11 @@
@using System.Reflection
@using LiteCharmsSecurity.STS.Identity
@{
var runtimeVersion = typeof(Startup)
.GetTypeInfo()
.Assembly
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
}
@runtimeVersion
@@ -0,0 +1,27 @@
@using Microsoft.AspNetCore.Builder
@using Microsoft.AspNetCore.Localization
@using Microsoft.AspNetCore.Mvc.Localization
@using Microsoft.Extensions.Options
@inject IViewLocalizer Localizer
@inject IOptions<RequestLocalizationOptions> LocOptions
@{
var requestCulture = Context.Features.Get<IRequestCultureFeature>();
var cultureItems = LocOptions.Value.SupportedUICultures
.Select(c => new SelectListItem { Value = c.Name, Text = c.DisplayName })
.ToList();
var returnUrl = string.IsNullOrEmpty(Context.Request.Path) ? "~/" : $"~{Context.Request.Path.Value}{Context.Request.QueryString}";
}
@if (cultureItems.Count > 1)
{
<div title="@Localizer["Request culture provider:"] @requestCulture?.Provider?.GetType().Name" class="w-full md:w-auto">
<form id="selectLanguageForm" asp-controller="Home"
asp-action="SetLanguage" asp-route-returnUrl="@returnUrl"
method="post" class="flex items-center gap-3" role="form">
<label asp-for="@requestCulture.RequestCulture.UICulture.Name" class="text-sm font-medium text-muted-foreground whitespace-nowrap">
<lucideicon name="languages" class="inline-block h-4 w-4 mr-1" />
@Localizer["Language:"]
</label>
<select name="culture" id="cultureSelect" class="select min-w-[150px]" asp-for="@requestCulture.RequestCulture.UICulture.Name" asp-items="cultureItems"></select>
</form>
</div>
}
@@ -0,0 +1,11 @@
@using Microsoft.AspNetCore.Mvc.Localization
@model string
@inject IViewLocalizer Localizer
@using Microsoft.AspNetCore.Authorization
@using LiteCharmsSecurity.STS.Identity.Configuration.Constants
@inject IAuthorizationService AuthorizationService
@if ((await AuthorizationService.AuthorizeAsync(User, AuthorizationConsts.AdministrationPolicy)).Succeeded)
{
<a class="btn-outline my-2 inline-flex items-center gap-2" href="@Model">@Localizer["IdentityServerAdmin"]</a>
}
@@ -0,0 +1,45 @@
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
@using Microsoft.AspNetCore.Hosting
@using Microsoft.Extensions.Hosting
@model LiteCharmsSecurity.STS.Identity.ViewModels.Home.ErrorViewModel
@inject IWebHostEnvironment Host
@{
var error = Model?.Error?.Error;
var errorDescription = Host.IsDevelopment() ? Model?.Error?.ErrorDescription : null;
var request_id = Model?.Error?.RequestId;
}
<div class="py-8">
<h1 class="text-4xl font-bold mb-6">@Localizer["Title"]</h1>
<div class="max-w-2xl">
<div class="alert-destructive">
<lucideicon name="alert-circle" class="h-4 w-4" />
<div>
<h5 class="alert-title">@Localizer["Message"]</h5>
<div class="alert-description">
@if (error != null)
{
<strong>
<em>
: @error
</em>
</strong>
if (errorDescription != null)
{
<div class="mt-2">@errorDescription</div>
}
}
</div>
</div>
</div>
@if (request_id != null)
{
<div class="mt-4 text-sm text-muted-foreground">@Localizer["Request"] @request_id</div>
}
</div>
</div>
@@ -0,0 +1,9 @@
@model LiteCharmsSecurity.STS.Identity.ViewModels.Account.RedirectViewModel
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
<div class="py-8 text-center">
<h1 class="text-4xl font-bold mb-2">@Localizer["Title"]</h1>
<p class="text-lg text-muted-foreground">@Localizer["SubTitle"]</p>
</div>
<meta http-equiv="refresh" content="0;url=@Model.RedirectUrl" data-url="@Model.RedirectUrl">
<script src="~/js/signin-redirect.js"></script>
@@ -0,0 +1,253 @@
@using Duende.IdentityServer.Extensions
@using Microsoft.AspNetCore.Identity
@using Microsoft.AspNetCore.Mvc.Localization
@using LiteCharmsSecurity.Admin.EntityFramework.Shared.Entities.Identity
@using LiteCharmsSecurity.STS.Identity.Configuration.Interfaces
@using LiteCharmsSecurity.STS.Identity.Configuration
@using Duende.IdentityServer
@using Microsoft.Extensions.Options
@inject IRootConfiguration RootConfiguration
@inject IViewLocalizer Localizer
@inject IOptions<ServerSideSessionsConfiguration> ServerSideSessionsOptions
@{
string name = null;
if (!true.Equals(ViewData["signed-out"]))
{
name = Context.User?.GetDisplayName();
}
}
@inject SignInManager<UserIdentity> SignInManager
@{
var hasExternalLogins = (await SignInManager.GetExternalAuthenticationSchemesAsync()).Any();
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@RootConfiguration.AdminConfiguration.PageTitle</title>
<link rel="icon" type="image/x-icon" href="@UrlHelper.Content(RootConfiguration.AdminConfiguration.FaviconUri)" />
<link rel="shortcut icon" type="image/x-icon"
href="@UrlHelper.Content(@RootConfiguration.AdminConfiguration.FaviconUri)" />
<script src="~/js/theme-init.js" asp-append-version="true"></script>
<link href="~/css/app.css" asp-append-version="true" rel="stylesheet" />
@if (!string.IsNullOrEmpty(RootConfiguration.AdminConfiguration.CustomThemeCss))
{
<link href="@UrlHelper.Content(RootConfiguration.AdminConfiguration.CustomThemeCss)" asp-append-version="true"
rel="stylesheet" />
}
@await RenderSectionAsync("styles", required: false)
</head>
<body class="flex min-h-screen flex-col bg-background text-foreground">
<header
class="sticky top-0 z-50 border-b bg-background/80 backdrop-blur supports-[backdrop-filter]:bg-background/60">
<nav class="container flex h-16 items-center">
<a class="text-lg font-semibold tracking-tight hover:opacity-90" asp-area="" asp-controller="Home"
asp-action="Index">
@RootConfiguration.AdminConfiguration.PageTitle
</a>
<div class="ml-auto hidden items-center gap-4 md:flex">
@if (User.Identity.IsAuthenticated && !string.IsNullOrEmpty(name))
{
<vc:identity-server-admin-link></vc:identity-server-admin-link>
<details class="relative group">
<summary class="list-none cursor-pointer select-none">
<span class="btn-outline inline-flex items-center gap-2">
<lucideicon name="cog" class="h-4 w-4" />
<span>@Localizer["Settings"] - @name</span>
<lucideicon name="chevron-down" class="h-4 w-4" />
</span>
</summary>
<div class="card absolute right-0 mt-2 w-64 overflow-hidden">
<div class="p-1">
<a class="block rounded-md px-3 py-2 text-sm hover:bg-accent hover:text-accent-foreground"
asp-controller="Manage" asp-action="Index">
<span class="inline-flex items-center gap-2">
<lucideicon name="user" class="h-4 w-4" />
@Localizer["ManageProfile"]
</span>
</a>
<a class="block rounded-md px-3 py-2 text-sm hover:bg-accent hover:text-accent-foreground"
asp-controller="Manage" asp-action="PersonalData">
<span class="inline-flex items-center gap-2">
<lucideicon name="id-card" class="h-4 w-4" />
@Localizer["ManagePersonalData"]
</span>
</a>
@if (hasExternalLogins)
{
<a class="block rounded-md px-3 py-2 text-sm hover:bg-accent hover:text-accent-foreground"
asp-controller="Manage" asp-action="ExternalLogins">
<span class="inline-flex items-center gap-2">
<lucideicon name="link-2" class="h-4 w-4" />
@Localizer["ExternalLogins"]
</span>
</a>
}
<a class="block rounded-md px-3 py-2 text-sm hover:bg-accent hover:text-accent-foreground"
asp-controller="Manage" asp-action="TwoFactorAuthentication">
<span class="inline-flex items-center gap-2">
<lucideicon name="shield-check" class="h-4 w-4" />
@Localizer["2FA"]
</span>
</a>
<a class="block rounded-md px-3 py-2 text-sm hover:bg-accent hover:text-accent-foreground"
asp-controller="Manage" asp-action="Passkeys">
<span class="inline-flex items-center gap-2">
<lucideicon name="fingerprint" class="h-4 w-4" />
@Localizer["Passkeys"]
</span>
</a>
<a class="block rounded-md px-3 py-2 text-sm hover:bg-accent hover:text-accent-foreground"
asp-controller="Manage" asp-action="ChangePassword">
<span class="inline-flex items-center gap-2">
<lucideicon name="key-round" class="h-4 w-4" />
@Localizer["ChangePassword"]
</span>
</a>
<a class="block rounded-md px-3 py-2 text-sm hover:bg-accent hover:text-accent-foreground"
asp-controller="Grants" asp-action="Index">
<span class="inline-flex items-center gap-2">
<lucideicon name="badge-check" class="h-4 w-4" />
@Localizer["Grants"]
</span>
</a>
@if (ServerSideSessionsOptions.Value.Enabled)
{
<a class="block rounded-md px-3 py-2 text-sm hover:bg-accent hover:text-accent-foreground"
asp-controller="ServerSideSessions" asp-action="Index">
<span class="inline-flex items-center gap-2">
<lucideicon name="box" class="h-4 w-4" />
@Localizer["ServerSessionsMenu"]
</span>
</a>
}
<div class="my-1 h-px bg-border"></div>
<a class="block rounded-md px-3 py-2 text-sm hover:bg-accent hover:text-accent-foreground"
asp-action="Logout" asp-controller="Account">
<span class="inline-flex items-center gap-2">
<lucideicon name="log-out" class="h-4 w-4" />
@Localizer["Signout"]
</span>
</a>
</div>
</div>
</details>
}
else
{
<a class="btn-outline inline-flex items-center gap-2" asp-action="Login" asp-controller="Account">
<lucideicon name="log-in" class="h-4 w-4" />
@Localizer["Login"]
</a>
@if (RootConfiguration.RegisterConfiguration.Enabled)
{
<a class="btn-outline inline-flex items-center gap-2" asp-action="Register" asp-controller="Account">
<lucideicon name="user-plus" class="h-4 w-4" />
@Localizer["Register"]
</a>
}
}
<button id="theme-toggle" class="btn-ghost btn-icon" aria-label="Toggle theme">
<span id="theme-toggle-dark-icon" class="hidden">
<lucideicon name="moon" class="h-5 w-5" />
</span>
<span id="theme-toggle-light-icon" class="hidden">
<lucideicon name="sun" class="h-5 w-5" />
</span>
</button>
</div>
<details class="ml-auto md:hidden">
<summary class="list-none">
<button class="btn-outline inline-flex items-center gap-2">
<lucideicon name="menu" class="h-5 w-5" />
@Localizer["Menu"]
</button>
</summary>
<div class="mt-3 space-y-2">
@if (User.Identity.IsAuthenticated && !string.IsNullOrEmpty(name))
{
<vc:identity-server-admin-link></vc:identity-server-admin-link>
<a class="block rounded-md px-3 py-2 text-sm hover:bg-accent hover:text-accent-foreground"
asp-controller="Manage" asp-action="Index">@Localizer["ManageProfile"]</a>
<a class="block rounded-md px-3 py-2 text-sm hover:bg-accent hover:text-accent-foreground"
asp-controller="Manage" asp-action="PersonalData">@Localizer["ManagePersonalData"]</a>
@if (hasExternalLogins)
{
<a class="block rounded-md px-3 py-2 text-sm hover:bg-accent hover:text-accent-foreground"
asp-controller="Manage" asp-action="ExternalLogins">@Localizer["ExternalLogins"]</a>
}
<a class="block rounded-md px-3 py-2 text-sm hover:bg-accent hover:text-accent-foreground"
asp-controller="Manage" asp-action="TwoFactorAuthentication">@Localizer["2FA"]</a>
<a class="block rounded-md px-3 py-2 text-sm hover:bg-accent hover:text-accent-foreground"
asp-controller="Manage" asp-action="Passkeys">@Localizer["Passkeys"]</a>
<a class="block rounded-md px-3 py-2 text-sm hover:bg-accent hover:text-accent-foreground"
asp-controller="Manage" asp-action="ChangePassword">@Localizer["ChangePassword"]</a>
<a class="block rounded-md px-3 py-2 text-sm hover:bg-accent hover:text-accent-foreground"
asp-controller="Grants" asp-action="Index">@Localizer["Grants"]</a>
@if (ServerSideSessionsOptions.Value.Enabled)
{
<a class="block rounded-md px-3 py-2 text-sm hover:bg-accent hover:text-accent-foreground"
asp-controller="ServerSideSessions" asp-action="Index">@Localizer["ServerSessionsMenu"]</a>
}
<a class="block rounded-md px-3 py-2 text-sm hover:bg-accent hover:text-accent-foreground"
asp-action="Logout" asp-controller="Account">@Localizer["Signout"]</a>
}
else
{
<a class="btn-outline inline-flex items-center gap-2" asp-action="Login" asp-controller="Account">
<lucideicon name="log-in" class="h-4 w-4" />
@Localizer["Login"]
</a>
@if (RootConfiguration.RegisterConfiguration.Enabled)
{
<a class="btn-outline inline-flex items-center gap-2" asp-action="Register" asp-controller="Account">
<lucideicon name="user-plus" class="h-4 w-4" />
@Localizer["Register"]
</a>
}
}
<button id="theme-toggle-mobile" class="btn-ghost btn-icon" aria-label="Toggle theme">
<span id="theme-toggle-dark-icon-mobile" class="hidden">
<lucideicon name="moon" class="h-5 w-5" />
</span>
<span id="theme-toggle-light-icon-mobile" class="hidden">
<lucideicon name="sun" class="h-5 w-5" />
</span>
</button>
</div>
</details>
</nav>
</header>
<div class="container flex-1 px-4 md:px-6">
<main role="main" class="py-6">
@RenderBody()
</main>
</div>
<footer class="mt-auto border-t">
<div class="container px-4 py-6 md:px-6">
<div class="grid gap-6 md:grid-cols-3 md:items-center">
<div class="space-y-1">
<div class="font-semibold">@RootConfiguration.AdminConfiguration.PageTitle</div>
<small class="text-muted-foreground">@Localizer["FooterCopyright"] @DateTime.Now.Year - @(await Html.PartialAsync("Common/ApplicationVersion"))</small>
</div>
<div></div>
<div class="flex justify-center md:justify-end">
@await Html.PartialAsync("Common/SelectLanguage")
</div>
</div>
</div>
</footer>
<script src="~/js/layout.js" asp-append-version="true"></script>
<script src="~/js/language.js" asp-append-version="true"></script>
@await RenderSectionAsync("scripts", required: false)
</body>
</html>
@@ -0,0 +1,47 @@
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
@model LiteCharmsSecurity.STS.Identity.ViewModels.Consent.ScopeViewModel
<li class="border-b last:border-0 pb-4 last:pb-0">
<div class="flex items-start justify-between gap-4">
<div class="flex-1">
@if (Model.Required)
{
<input type="hidden"
name="ScopesConsented"
value="@Model.Value" />
}
<div class="flex items-center gap-2 mb-1">
<strong class="font-semibold">@Model.DisplayName</strong>
@if (Model.Emphasize)
{
<lucideicon name="alert-circle" class="h-4 w-4 text-destructive" />
}
</div>
@if (Model.Required)
{
<div class="text-sm text-muted-foreground italic mb-1">
@Localizer["Required"]
</div>
}
@if (Model.Description != null)
{
<label for="scopes_@Model.Value" class="text-sm text-muted-foreground cursor-pointer">
@Model.Description
</label>
}
</div>
<div class="flex items-center pt-1">
<label class="switch">
<input class="consent-scopecheck"
type="checkbox"
name="ScopesConsented"
id="scopes_@Model.Value"
value="@Model.Value"
checked="@Model.Checked"
disabled="@Model.Required" />
<span class="switch-slider"></span>
</label>
</div>
</div>
</li>
@@ -0,0 +1,14 @@
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
@if (ViewContext.ModelState.IsValid == false)
{
<div class="alert-destructive mb-6">
<lucideicon name="alert-circle" class="h-4 w-4" />
<div>
<h5 class="alert-title">@Localizer["Title"]</h5>
<div class="alert-description">
<div asp-validation-summary="All"></div>
</div>
</div>
</div>
}
@@ -0,0 +1,3 @@
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, LiteCharmsSecurity.STS.Identity
@inject IUrlHelper UrlHelper
@@ -0,0 +1,3 @@
@{
Layout = "_Layout";
}