fix: harden SSO ticket flow and revoke tickets on logout
CI / check (push) Successful in 28s
CI / release (push) Skipped
CI / deploy (push) Successful in 54s

Reuse the outstanding auth_ticket instead of minting a fresh one on every
/client load, so reloading the page or opening a second tab no longer
invalidates a game session that is still connecting. New tickets are minted
with a guard against the previously-read value so concurrent launches
converge on the same ticket.

Revoke the auth_ticket when signing out (toolbar, header and sign-out
everywhere) so a leaked ticket can no longer be replayed against the
emulator, and prevent SSO leakage via referral by setting no-referrer on the
client iframe. Strip all whitespace from the ticket prefix and build the
launch URL through a tested helper that handles query strings, existing sso
params and URL fragments correctly.
This commit is contained in:
openhands committed 2026-08-29 20:54:06 +02:00
1 parent ca59a1065f
commit 7f39ba4257
7 files changed
+231 -28

No files matched your search

+5 -5
View File
@@ -1,7 +1,6 @@
"use client";
import { LogOut } from "lucide-react";
import { signOut } from "next-auth/react";
import {
type ReactNode,
useCallback,
@@ -10,7 +9,9 @@ import {
useRef,
useState,
} from "react";
import { signOutAndRevokeTicket } from "@/actions/sessions";
import Link from "@/components/link";
import { buildClientLoginUrl } from "@/lib/client-url";
function ToolbarBtn({
onClick,
@@ -94,9 +95,7 @@ export function ClientView({
return () => document.removeEventListener("fullscreenchange", onChange);
}, []);
const base = clientUrl.replace(/(\?|&)sso=[^&]*/, "");
const sep = base.includes("?") ? "&" : "?";
const clientSrc = `${base}${sep}sso=${encodeURIComponent(ticket)}`;
const clientSrc = buildClientLoginUrl(clientUrl, ticket);
const toolbarRef = useRef<HTMLDivElement>(null);
const dragRef = useRef({ startX: 0, startY: 0, startTop: 0, startLeft: 0 });
@@ -361,7 +360,7 @@ export function ClientView({
</svg>
</div>
<ToolbarBtn
onClick={() => signOut({ callbackUrl: "/" })}
onClick={() => void signOutAndRevokeTicket()}
title="Logout"
>
<LogOut size={14} />
@@ -382,6 +381,7 @@ export function ClientView({
}`}
title={hotelName}
allow="autoplay; gamepad; fullscreen"
referrerPolicy="no-referrer"
loading="eager"
/>
</div>