feat: postMessage auth token relay for cross-origin federation login

- Backend: AuthMiddleware.dispatch() checks X-Muxplex-Token header
  after cookie auth, allowing cross-origin federation requests to
  authenticate using a session token sent as a header instead of a
  SameSite=Strict cookie.

- Backend: GET /api/auth/token endpoint returns the current session
  token for authenticated requests. Used by login popups to relay
  the token back to the opener window via postMessage.

- Frontend (app.js): api() injects X-Muxplex-Token header for
  cross-origin requests when a token is stored in localStorage under
  muxplex.federation_tokens keyed by origin.

- Frontend (app.js): storeFederationToken() helper stores tokens.
  window.addEventListener('message') handler catches muxplex-auth-token
  postMessages and stores the token, then retriggers a poll so the
  auth_required source transitions to authenticated immediately.

- Frontend (app.js): _saveRemoteInstances() prunes stale tokens for
  URLs no longer in the remote instances list.

- Frontend (index.html): Popup relay script fetches /api/auth/token
  and sends the token to window.opener via postMessage when loaded
  as a login popup (window.opener present).

Tests added:
  test_auth.py: X-Muxplex-Token header authenticates / invalid rejected
  test_api.py: GET /api/auth/token returns token / 401 when unauthed
  test_app.mjs: api() header injection, storeFederationToken storage,
                index.html popup script presence
This commit is contained in:
Brian Krabach
2026-04-01 08:17:28 -07:00
parent 062a815f83
commit cb264bbaaa
23 changed files with 237 additions and 1 deletions
+20
View File
@@ -15,6 +15,26 @@
<title>muxplex</title>
</head>
<body>
<script>
// Federation auth: if opened as a login popup by another muxplex instance,
// relay the session token back to the opener window via postMessage.
(function() {
if (!window.opener) return;
fetch('/api/auth/token')
.then(function(r) { return r.ok ? r.json() : null; })
.then(function(data) {
if (data && data.token) {
window.opener.postMessage({
type: 'muxplex-auth-token',
origin: window.location.origin,
token: data.token
}, '*');
setTimeout(function() { window.close(); }, 500);
}
})
.catch(function() {});
})();
</script>
<!-- ── Overview view ──────────────────────────────────────────────────── -->
<div id="view-overview" class="view view--active">