app: OTM integrations — operator-magic + account SSO + suggestion jots + self-update (v0.4.0)
Three new doors, all wire-contract ports of the shared @otm/account-panel factories (this backbone is plain Node — the Next factories can't mount): - GET /api/auth/operator-magic — OTM 'Log in as admin'. HS256 verify against OPERATOR_SHARED_SECRET (alg allowlist, constant-time, action-claim rejected for flow separation), single-use jti via OperatorMagicConsumed (+ who/when audit), 1-HOUR session with cookie Max-Age derived from the payload, 🔑 support-session banner in the app, every failure a 302 reason redirect. - GET /api/auth/otm-sso — the OTM /account tile. Stricter sso-ticket verify (exp mandatory, audience compared), email-then-role actor mapping, safeNext. - 💡 suggestion jot chip — files a Gitea issue (shared attribution footer, 8k cap, 10s timeout); on any failure the jot is kept as a note instead. Success also leaves a '#N — …' note so she has her own record. - Footer version chip + one-tap self-update (HMAC-signed OTM proxy with explicit field picking — foreign JSON can never reach the _setSession/_redirect control keys), owner-gated, 10-min sessionStorage cache on the check. Hardening that rode along: session key now derived from OPERATOR_SHARED_SECRET (managed boot REFUSES the old derivable DATABASE_URL fallback), 1MB JSON body cap, login.html prototype-lookup fix. NOTE: server.mjs previously contained a literal NUL byte that made git treat it as binary — this commit re-encodes it as an escape (behavior identical) and adds .gitattributes so source diffs can never go blind again. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SmybqyQmZWfcqA1vMP4jbQ
This commit is contained in:
@@ -35,6 +35,14 @@
|
||||
</form>
|
||||
</div>
|
||||
<script>
|
||||
// ?reason=… arrives when an OTM "Log in as admin" link didn't take
|
||||
const REASONS = { not_enabled: "admin sign-in isn't set up for this studio yet",
|
||||
invalid_token: "that sign-in link isn't valid", link_already_used: "that sign-in link was already used — ask for a fresh one",
|
||||
no_admin: "there's no owner account here to sign in as", no_account: "no studio account matches your OTM account yet",
|
||||
server_error: "something went wrong — try again" };
|
||||
const reason = new URLSearchParams(location.search).get("reason");
|
||||
if (typeof REASONS[reason] === "string") document.getElementById("err").textContent = REASONS[reason];
|
||||
|
||||
document.getElementById("f").addEventListener("submit", async (e) => {
|
||||
e.preventDefault();
|
||||
const r = await fetch("/api/login", { method: "POST", headers: { "content-type": "application/json" },
|
||||
|
||||
Reference in New Issue
Block a user