"use client"; import { useState, useMemo, type FormEvent } from "react"; import { useSearchParams } from "next/navigation"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Smartphone, AlertCircle, Loader2 } from "lucide-react"; // Only redirect back to the official mobile-app scheme. Without this guard // the page becomes an open redirector that funnels arbitrary credentials to // any URL an attacker chooses. const ALLOWED_REDIRECT_PREFIX = "bulwarkmobile://"; export default function MobileHandoffPage() { const searchParams = useSearchParams(); const redirectUri = searchParams.get("redirect_uri") ?? ""; const state = searchParams.get("state") ?? ""; const redirectOk = useMemo( () => redirectUri.startsWith(ALLOWED_REDIRECT_PREFIX), [redirectUri], ); const [serverUrl, setServerUrl] = useState(""); const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); const [error, setError] = useState(null); const [busy, setBusy] = useState(false); const canSubmit = serverUrl.trim() && username.trim() && password; const handleSubmit = async (e: FormEvent) => { e.preventDefault(); if (!canSubmit) return; setError(null); setBusy(true); try { const trimmedServerUrl = serverUrl.trim().replace(/\/+$/, ""); const verifyRes = await fetch("/api/auth/mobile-verify", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ serverUrl: trimmedServerUrl, username: username.trim(), password, }), }); const verifyJson = await verifyRes.json().catch(() => ({})); if (!verifyRes.ok) { setError(verifyJson.error || "Sign-in failed"); setBusy(false); return; } // Build the callback URL with credentials in the fragment so they // don't end up in HTTP referrer logs along the way. const verifiedUrl = (verifyJson.serverUrl as string) || trimmedServerUrl; const fragment = new URLSearchParams({ server_url: verifiedUrl, username: username.trim(), password, state, }); window.location.href = `${redirectUri}#${fragment.toString()}`; } catch (err) { setError(err instanceof Error ? err.message : "Sign-in failed"); setBusy(false); } }; if (!redirectOk) { return (

Invalid request

The mobile app sent an unrecognized callback URL. Update the app and try again.

); } return (

Sign in to Bulwark Mobile

Enter your credentials. They'll be handed off to the app and you'll be returned automatically.

{error ? (
{error}
) : null}
); }