Feature: Add admin-only SSO bridge to web UI
Bridge an already-authenticated portal session into a YAMA web session via a new /sso endpoint, so portal administrators can enter the remote-desktop UI without a second login. Only the "admin" portal role is bridged; any other role (viewer, unknown) still falls back to the built-in login form, so a low-privileged portal account cannot bypass authentication. - wsauth: extract IssueToken from VerifyLogin so an out-of-band-authenticated identity (SSO) can mint a session token without the challenge/response. - web: add the SSODetector interface, a fail-closed JWT detector (HMAC-only), and the /sso handler gated to the admin role. - cmd: enable the JWT detector via YAMA_SSO_JWT_SECRET, the shared HS256 secret matching the parent portal. - web UI: attempt SSO before showing the login form, falling back to login when the server returns 401. - docs: document SSO (admin-only) and license trial-vs-paid troubleshooting in README and the env example. Co-Authored-By: deepseek-v4-pro
This commit is contained in:
1 parent
d2149851b2
commit
53b7151fb7
9 files changed
+499
-11
No files matched your search
+33
-6
@@ -1670,6 +1670,26 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt single sign-on via the portal's session cookie. On success
|
||||
// the server returns a YAMA session token + role, which we persist
|
||||
// exactly like a local login; the caller then connects the WS and skips
|
||||
// the login form. Returns true on success, false to fall back to login.
|
||||
async function trySSO() {
|
||||
try {
|
||||
const resp = await fetch('__BASE_PATH__/sso', { credentials: 'same-origin' });
|
||||
if (!resp.ok) return false;
|
||||
const data = await resp.json();
|
||||
if (!data || !data.token) return false;
|
||||
token = data.token;
|
||||
currentUserRole = data.role || 'viewer';
|
||||
sessionStorage.setItem('token', token);
|
||||
sessionStorage.setItem('role', currentUserRole);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function connectWebSocket() {
|
||||
if (!isPageVisible) return; // Don't connect in background
|
||||
const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
@@ -4480,14 +4500,21 @@
|
||||
bindKeyboardBtnEvents(document.getElementById('qc-keyboard'));
|
||||
bindKeyboardBtnEvents(document.getElementById('btn-keyboard'));
|
||||
bindKeyboardBtnEvents(document.getElementById('btn-keyboard-bar'));
|
||||
// Restore token and role from sessionStorage
|
||||
// Restore token and role from sessionStorage (local-login fallback).
|
||||
token = sessionStorage.getItem('token');
|
||||
currentUserRole = sessionStorage.getItem('role') || 'viewer';
|
||||
// Show Users button for admin only (will be updated after login verification)
|
||||
if (token && currentUserRole === 'admin') {
|
||||
document.getElementById('users-btn').classList.add('visible');
|
||||
}
|
||||
connectWebSocket();
|
||||
// Attempt portal SSO first: if the user is already logged into the
|
||||
// parent domain, the server bridges the portal session cookie into
|
||||
// a YAMA session token so the login form is skipped. On failure
|
||||
// (not logged in, or SSO disabled) fall back to the restored
|
||||
// session or the built-in login form.
|
||||
trySSO().then(() => {
|
||||
// Show Users button for admin only (final role known after SSO).
|
||||
if (token && currentUserRole === 'admin') {
|
||||
document.getElementById('users-btn').classList.add('visible');
|
||||
}
|
||||
connectWebSocket();
|
||||
});
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
|
||||
Reference in new issue
Block a user