Add per-user TOTP 2FA, client-level user assignment, self-service portal
- TOTP (RFC 6238, stdlib-only) enrollment in profile, login step-up, admin emergency reset. - Admins can grant a user visibility into individual clients (User.ClientIDs) in addition to whole-server access (User.ServerIDs). - New "My Access" page: non-admin users see only their assigned clients (view/QR/download only, no management), reachable from the main nav. - GetUser/GetUsers now redact TOTPSecret before returning JSON. No Go toolchain was available while writing this - not yet build-verified. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PvrfUytqd74H6WcQkRzFM4
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
c29edfdcc3
commit
34bc8f76f9
+52
-1
@@ -64,6 +64,22 @@
|
||||
<!-- /.col -->
|
||||
</div>
|
||||
</form>
|
||||
<form id="totp-form" action="" method="post" style="display:none;">
|
||||
<p class="login-box-msg">Enter the 6-digit code from your authenticator app</p>
|
||||
<div class="input-group mb-3">
|
||||
<input id="totp_code" type="text" inputmode="numeric" autocomplete="one-time-code" maxlength="6" class="form-control" placeholder="123456">
|
||||
<div class="input-group-append">
|
||||
<div class="input-group-text">
|
||||
<span class="fas fa-shield-alt"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<button id="btn_totp" type="submit" class="btn btn-primary btn-block">Verify</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="text-center mb-3">
|
||||
<p id="message"></p>
|
||||
</div>
|
||||
@@ -93,11 +109,16 @@
|
||||
</script>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$('form').on('submit', function(e) {
|
||||
$('#username, #password').closest('form').on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
$("#btn_login").trigger('click');
|
||||
});
|
||||
|
||||
$('#totp-form').on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
$("#btn_totp").trigger('click');
|
||||
});
|
||||
|
||||
$("#btn_login").click(function () {
|
||||
const username = $("#username").val();
|
||||
const password = $("#password").val();
|
||||
@@ -114,6 +135,36 @@
|
||||
dataType: 'json',
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify(data),
|
||||
success: function(data) {
|
||||
if (data['totp_required']) {
|
||||
document.getElementById("message").innerHTML = "";
|
||||
$('#username').closest('form').hide();
|
||||
$('#totp-form').show();
|
||||
$('#totp_code').focus();
|
||||
return;
|
||||
}
|
||||
document.getElementById("message").innerHTML = `<p style="color:green">${data['message']}</p>`;
|
||||
// redirect after logging in successfully
|
||||
redirectNext();
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
const responseJson = jQuery.parseJSON(jqXHR.responseText);
|
||||
document.getElementById("message").innerHTML = `<p style="color:#ff0000">${responseJson['message']}</p>`;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$("#btn_totp").click(function () {
|
||||
const code = $("#totp_code").val();
|
||||
const data = {"code": code}
|
||||
|
||||
$.ajax({
|
||||
cache: false,
|
||||
method: 'POST',
|
||||
url: '{{.basePath}}/login/totp',
|
||||
dataType: 'json',
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify(data),
|
||||
success: function(data) {
|
||||
document.getElementById("message").innerHTML = `<p style="color:green">${data['message']}</p>`;
|
||||
// redirect after logging in successfully
|
||||
|
||||
Reference in New Issue
Block a user