fix(console): send proxy tier headers for remote node console-token requests (#424)

The gateway's console-token fetch to remote nodes was missing the
x-sencho-tier and x-sencho-variant headers. Without them, the remote's
requireAdmiral guard fell back to its own local license (Community),
rejecting the request with 403 and surfacing as a 502 "Connection error"
in the browser.

Additionally, the remote's WS upgrade handler for host-console checked
only the local LicenseService instead of respecting proxy tier headers
on console_session tokens. Both paths now propagate and honour proxy
tier headers consistently.
This commit is contained in:
Anso
2026-04-08 09:46:14 -04:00
committed by GitHub
parent a6849aedab
commit 2354beed02
2 changed files with 20 additions and 3 deletions
+19 -3
View File
@@ -2690,9 +2690,14 @@ server.on('upgrade', async (req, socket, head) => {
let bearerTokenForProxy = node.api_token;
if (isInteractiveConsolePath) {
try {
const ls = LicenseService.getInstance();
const tokenRes = await fetch(`${node.api_url.replace(/\/$/, '')}/api/system/console-token`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${node.api_token}` },
headers: {
'Authorization': `Bearer ${node.api_token}`,
[PROXY_TIER_HEADER]: ls.getTier(),
[PROXY_VARIANT_HEADER]: ls.getVariant() || '',
},
});
if (tokenRes.ok) {
const data = await tokenRes.json() as { token?: string };
@@ -2759,9 +2764,20 @@ server.on('upgrade', async (req, socket, head) => {
socket.destroy();
return;
}
// Admiral license gate: host console requires Admiral (paid + team variant)
// Admiral license gate: host console requires Admiral (paid + team variant).
// For proxied connections (console_session tokens), trust the tier headers sent by the gateway;
// for direct connections, check the local LicenseService.
const isConsoleSession = decoded.scope === 'console_session';
const consoleTierHeader = req.headers[PROXY_TIER_HEADER] as string | undefined;
const consoleVariantHeader = req.headers[PROXY_VARIANT_HEADER] as string | undefined;
const ls = LicenseService.getInstance();
if (ls.getTier() !== 'paid' || ls.getVariant() !== 'admiral') {
const consoleTier = (isConsoleSession && isLicenseTier(consoleTierHeader))
? normalizeTier(consoleTierHeader)
: ls.getTier();
const consoleVariant = (isConsoleSession && consoleVariantHeader !== undefined && isLicenseVariant(consoleVariantHeader))
? normalizeVariant(consoleVariantHeader)
: ls.getVariant();
if (consoleTier !== 'paid' || consoleVariant !== 'admiral') {
socket.write('HTTP/1.1 403 Forbidden\r\n\r\n');
socket.destroy();
return;