mirror of
https://github.com/LD-Reborn/Berufsschule_HAM.git
synced 2025-12-19 22:41:55 +00:00
Added re-authentication modal
This commit is contained in:
@@ -166,6 +166,7 @@ public class HomeController : Controller
|
|||||||
|
|
||||||
return RedirectToAction("Index", "Home");
|
return RedirectToAction("Index", "Home");
|
||||||
}
|
}
|
||||||
|
Response.StatusCode = 500;
|
||||||
switch (authenticationResult.AuthenticationState)
|
switch (authenticationResult.AuthenticationState)
|
||||||
{
|
{
|
||||||
case UserNotAuthenticatedReason.InvalidCredentials:
|
case UserNotAuthenticatedReason.InvalidCredentials:
|
||||||
@@ -198,4 +199,18 @@ public class HomeController : Controller
|
|||||||
{
|
{
|
||||||
return View();
|
return View();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Authorize]
|
||||||
|
[HttpGet("RemainingTime")]
|
||||||
|
public async Task<IActionResult> GetRemainingSessionTime()
|
||||||
|
{
|
||||||
|
var result = await HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||||
|
|
||||||
|
if (!result.Succeeded || result.Properties?.ExpiresUtc == null)
|
||||||
|
return Json(new { remainingMinutes = 0 });
|
||||||
|
|
||||||
|
var remaining = result.Properties.ExpiresUtc.Value - DateTimeOffset.UtcNow;
|
||||||
|
return Json(new { remainingMinutes = (int) Math.Ceiling(remaining.TotalMinutes) });
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -19,12 +19,6 @@
|
|||||||
<data name="Login" xml:space="preserve">
|
<data name="Login" xml:space="preserve">
|
||||||
<value>Anmelden</value>
|
<value>Anmelden</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Username" xml:space="preserve">
|
|
||||||
<value>Benutzername</value>
|
|
||||||
</data>
|
|
||||||
<data name="Password" xml:space="preserve">
|
|
||||||
<value>Passwort</value>
|
|
||||||
</data>
|
|
||||||
<data name="Invalid login credentials" xml:space="preserve">
|
<data name="Invalid login credentials" xml:space="preserve">
|
||||||
<value>Ungültige Anmeldedaten</value>
|
<value>Ungültige Anmeldedaten</value>
|
||||||
</data>
|
</data>
|
||||||
|
|||||||
@@ -61,4 +61,28 @@
|
|||||||
<data name="Admin settings" xml:space="preserve">
|
<data name="Admin settings" xml:space="preserve">
|
||||||
<value>Administration</value>
|
<value>Administration</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Extend session duration" xml:space="preserve">
|
||||||
|
<value>Sitzung verlängern</value>
|
||||||
|
</data>
|
||||||
|
<data name="The session expires soon." xml:space="preserve">
|
||||||
|
<value>Die Sitzung läuft bald ab.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Please authenticate to continue without losing data." xml:space="preserve">
|
||||||
|
<value>Bitte authentifizieren Sie sich, um fortzufahren, ohne Daten zu verlieren.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Re-authenticate" xml:space="preserve">
|
||||||
|
<value>Authentifizieren</value>
|
||||||
|
</data>
|
||||||
|
<data name="Invalid login credentials" xml:space="preserve">
|
||||||
|
<value>Ungültige Anmeldedaten</value>
|
||||||
|
</data>
|
||||||
|
<data name="Your account has been locked. Wait a few minutes or ask an administrator to unlock you" xml:space="preserve">
|
||||||
|
<value>Ihr Konto wurde gesperrt. Warten Sie einige Minuten oder bitten Sie einen Administrator, die Sperre aufzuheben.</value>
|
||||||
|
</data>
|
||||||
|
<data name="You are not authorized for login. Ask an administrator to authorize you." xml:space="preserve">
|
||||||
|
<value>Sie sind nicht zur Anmeldung berechtigt. Bitten Sie einen Administrator, Ihnen die Berechtigung zu erteilen.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Hell froze over. Make a screenshot and send it to an administrator." xml:space="preserve">
|
||||||
|
<value>Die Hölle ist zugefroren. Machen Sie einen Screenshot und senden Sie ihn an einen Administrator.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|||||||
25
src/Resources/Views.Shared._Login.de.resx
Normal file
25
src/Resources/Views.Shared._Login.de.resx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, ...</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, ...</value>
|
||||||
|
</resheader>
|
||||||
|
|
||||||
|
<data name="Login" xml:space="preserve">
|
||||||
|
<value>Anmelden</value>
|
||||||
|
</data>
|
||||||
|
<data name="Username" xml:space="preserve">
|
||||||
|
<value>Benutzername</value>
|
||||||
|
</data>
|
||||||
|
<data name="Password" xml:space="preserve">
|
||||||
|
<value>Passwort</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
||||||
@@ -78,32 +78,32 @@
|
|||||||
const BARCODE_TYPE = "@barcodeType";
|
const BARCODE_TYPE = "@barcodeType";
|
||||||
if (!isEnteredManually)
|
if (!isEnteredManually)
|
||||||
{
|
{
|
||||||
switch (BARCODE_TYPE.toUpperCase()) {
|
switch (BARCODE_TYPE.toUpperCase()) {
|
||||||
case "EAN13":
|
case "EAN13":
|
||||||
decodedText = decodedText.slice(0,-1);
|
decodedText = decodedText.slice(0,-1);
|
||||||
break;
|
break;
|
||||||
case "EAN8":
|
case "EAN8":
|
||||||
decodedText = decodedText.slice(0,-1);
|
decodedText = decodedText.slice(0,-1);
|
||||||
break;
|
break;
|
||||||
case "UPC":
|
case "UPC":
|
||||||
decodedText = decodedText.slice(0,-1);
|
decodedText = decodedText.slice(0,-1);
|
||||||
break;
|
break;
|
||||||
case "ITF14":
|
case "ITF14":
|
||||||
decodedText = decodedText.slice(0,-1);
|
decodedText = decodedText.slice(0,-1);
|
||||||
break;
|
break;
|
||||||
case "MSI10":
|
case "MSI10":
|
||||||
decodedText = decodedText.slice(0,-1);
|
decodedText = decodedText.slice(0,-1);
|
||||||
break;
|
break;
|
||||||
case "MSI11":
|
case "MSI11":
|
||||||
decodedText = decodedText.slice(0,-1);
|
decodedText = decodedText.slice(0,-1);
|
||||||
break;
|
break;
|
||||||
case "MSI1010":
|
case "MSI1010":
|
||||||
decodedText = decodedText.slice(0,-2);
|
decodedText = decodedText.slice(0,-2);
|
||||||
break;
|
break;
|
||||||
case "MSI1110":
|
case "MSI1110":
|
||||||
decodedText = decodedText.slice(0,-1);
|
decodedText = decodedText.slice(0,-1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
decodedText = decodedText.replace(/^0+/, '');
|
decodedText = decodedText.replace(/^0+/, '');
|
||||||
console.log(`Code matched = ${decodedText}`, decodedResult);
|
console.log(`Code matched = ${decodedText}`, decodedResult);
|
||||||
|
|||||||
@@ -15,17 +15,5 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
<form method="post" action="/Home/Login" class="mt-4" style="max-width: 400px; margin: auto;">
|
<partial name="_Login" />
|
||||||
<div class="form-group mb-3">
|
|
||||||
<label for="username" class="form-label">@T["Username"]</label>
|
|
||||||
<input autofocus type="text" class="form-control" id="username" name="username" autocomplete="username" required>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group mb-3">
|
|
||||||
<label for="password" class="form-label">@T["Password"]</label>
|
|
||||||
<input type="password" class="form-control" id="password" name="password" autocomplete="current-password" required>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="submit" class="btn btn-primary w-100">@T["Login"]</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -139,6 +139,88 @@
|
|||||||
© 2025 - Berufsschule_HAM
|
© 2025 - Berufsschule_HAM
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<!-- Re-authentication Modal -->
|
||||||
|
<div class="modal fade" id="reAuthModal" tabindex="-1" aria-labelledby="reAuthModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header bg-info text-dark">
|
||||||
|
<h3 class="modal-title" id="detailModalLabel">@T["Extend session duration"]</h3>
|
||||||
|
<button type="button" class="btn-close btn-close-white" style="filter: invert(0);" aria-label="Close" data-bs-dismiss="modal"></button>
|
||||||
|
</div>
|
||||||
|
<form id="reAuthForm">
|
||||||
|
<div class="modal-body">
|
||||||
|
<p id="reAuthModalLabel" class="text-center">@T["The session expires soon."]<br/>@T["Please authenticate to continue without losing data."]</p>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="reAuthUsername" class="form-label">@T["Username"]</label>
|
||||||
|
<input type="text" class="form-control" id="reAuthUsername" name="Username"
|
||||||
|
value="@User.Identity?.Name" readonly />
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="reAuthPassword" class="form-label">@T["Password"]</label>
|
||||||
|
<input type="password" class="form-control" id="reAuthPassword" name="Password" required />
|
||||||
|
</div>
|
||||||
|
<div class="alert alert-danger d-none" id="reAuthError"></div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="submit" class="btn btn-primary">@T["Re-authenticate"]</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
// Flag so modal only opens once
|
||||||
|
window.reAuthModalOpen = false;
|
||||||
|
|
||||||
|
function openReAuthModal() {
|
||||||
|
window.reAuthModalOpen = true;
|
||||||
|
const reAuthModal = new bootstrap.Modal(document.getElementById('reAuthModal'));
|
||||||
|
reAuthModal.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
function scheduleReAuthModal() {
|
||||||
|
fetch('/Home/RemainingTime')
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {
|
||||||
|
let remainingMinutesThreshold = 20;
|
||||||
|
let remainingMinutes = data.remainingMinutes;
|
||||||
|
let triggerMinutes = Math.max(0, remainingMinutes - remainingMinutesThreshold);
|
||||||
|
let triggerMs = triggerMinutes * 60 * 1000;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!window.reAuthModalOpen) {
|
||||||
|
openReAuthModal();
|
||||||
|
}
|
||||||
|
}, triggerMs);
|
||||||
|
})
|
||||||
|
.catch(console.error);
|
||||||
|
}
|
||||||
|
scheduleReAuthModal();
|
||||||
|
|
||||||
|
$('#reAuthForm').on('submit', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const formData = $(this).serialize();
|
||||||
|
$.post('/Home/Login', formData)
|
||||||
|
.done(function () {
|
||||||
|
window.reAuthModalOpen = false;
|
||||||
|
$('#reAuthModal').modal('hide');
|
||||||
|
})
|
||||||
|
.fail(function (data) {
|
||||||
|
console.log(data);
|
||||||
|
const parser = new DOMParser();
|
||||||
|
const doc = parser.parseFromString(data.responseText, 'text/html');
|
||||||
|
const errorHeading = doc.querySelector("main div div h2");
|
||||||
|
let responseText = data.responseText;
|
||||||
|
document.getElementById("reAuthError").textContent = errorHeading.textContent;
|
||||||
|
$('#reAuthError').removeClass('d-none');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
@* <script src="~/lib/jquery/dist/jquery.min.js" defer></script> *@
|
@* <script src="~/lib/jquery/dist/jquery.min.js" defer></script> *@
|
||||||
<script src="https://code.jquery.com/jquery-3.7.1.min.js"
|
<script src="https://code.jquery.com/jquery-3.7.1.min.js"
|
||||||
crossorigin="anonymous" defer></script>
|
crossorigin="anonymous" defer></script>
|
||||||
|
|||||||
16
src/Views/Shared/_Login.cshtml
Normal file
16
src/Views/Shared/_Login.cshtml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
@using Microsoft.AspNetCore.Mvc.Localization
|
||||||
|
@inject IViewLocalizer T
|
||||||
|
|
||||||
|
<form method="post" action="/Home/Login" class="mt-4" style="max-width: 400px; margin: auto;">
|
||||||
|
<div class="form-group mb-3">
|
||||||
|
<label for="username" class="form-label">@T["Username"]</label>
|
||||||
|
<input autofocus type="text" class="form-control" id="username" name="username" autocomplete="username" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group mb-3">
|
||||||
|
<label for="password" class="form-label">@T["Password"]</label>
|
||||||
|
<input type="password" class="form-control" id="password" name="password" autocomplete="current-password" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary w-100">@T["Login"]</button>
|
||||||
|
</form>
|
||||||
Reference in New Issue
Block a user