- api: matrix-sso module (tuwunel jwt -> login/get_token), POST /matrix/session, GET /matrix/identity, drive controller - web: /chat full-height Element view, /files Library+Drive tabs, embedded-app frame component - edge: /hub-sso.html handshake route (origin-pinned CSP), X-Frame-Options strip on files - oCIS: csp.yaml frame-ancestors app.occupymedia.cc - server config still required: tuwunel [global.jwt], MATRIX_SSO_JWT_SECRET, oCIS restart
63 lines
2.7 KiB
HTML
63 lines
2.7 KiB
HTML
<!doctype html>
|
|
<!--
|
|
Hub → Element sign-in bootstrap, served by the edge at
|
|
https://chat.occupymedia.cc/hub-sso.html (same origin as Element Web).
|
|
|
|
Element only completes `/?loginToken=…` when localStorage["mx_sso_hs_url"]
|
|
was set on its own origin beforehand (Lifecycle.attemptTokenLogin — normally
|
|
Element writes it when IT starts an SSO redirect). The Hub cannot write
|
|
another origin's storage, so this page does it, then hands over.
|
|
|
|
Protocol (window.postMessage, both directions origin-pinned):
|
|
page → Hub { type: "tmbh:chat-sso:need-token" }
|
|
Hub → page { type: "tmbh:chat-sso:token", loginToken }
|
|
The Hub frames this page as /hub-sso.html#expect=<mxid>; when Element
|
|
already holds a live session for that MXID no token is requested at all, so
|
|
re-opening Chat never replaces the device (and its E2EE keys).
|
|
-->
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="referrer" content="no-referrer">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Signing in to chat…</title>
|
|
<style>html,body{margin:0;height:100%;background:#000;color:#9a9a9a;font:14px system-ui,sans-serif;display:grid;place-items:center}</style>
|
|
</head>
|
|
<body>
|
|
<p>Signing in to chat…</p>
|
|
<script>
|
|
(function () {
|
|
var HUB_ORIGINS = ["https://app.occupymedia.cc"];
|
|
// Element and the homeserver share this host (/_matrix is proxied here).
|
|
var HOMESERVER = location.origin;
|
|
|
|
function openElement(query) {
|
|
location.replace("/" + query);
|
|
}
|
|
|
|
// Opened directly rather than framed by the Hub: plain Element.
|
|
if (window.parent === window) return openElement("");
|
|
|
|
var expected = new URLSearchParams(location.hash.slice(1)).get("expect");
|
|
var signedInAs = localStorage.getItem("mx_user_id");
|
|
var live = localStorage.getItem("mx_has_access_token") === "true" && localStorage.getItem("mx_soft_logout") !== "true";
|
|
if (expected !== null && expected === signedInAs && live) return openElement("");
|
|
|
|
var done = false;
|
|
window.addEventListener("message", function (event) {
|
|
if (done || event.source !== window.parent || HUB_ORIGINS.indexOf(event.origin) === -1) return;
|
|
var data = event.data;
|
|
if (data === null || typeof data !== "object" || data.type !== "tmbh:chat-sso:token" || typeof data.loginToken !== "string") return;
|
|
done = true;
|
|
localStorage.setItem("mx_sso_hs_url", HOMESERVER);
|
|
localStorage.removeItem("mx_sso_is_url");
|
|
openElement("?loginToken=" + encodeURIComponent(data.loginToken));
|
|
});
|
|
// A non-matching targetOrigin is dropped by the browser, never delivered.
|
|
HUB_ORIGINS.forEach(function (origin) {
|
|
window.parent.postMessage({ type: "tmbh:chat-sso:need-token" }, origin);
|
|
});
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|