Cereal Milk

Sign in to unlock your inbox.

// Self-contained Better Auth sign-in page for Cereal Milk. // Served by the Better Auth server (same domain, so cookies work). // The desktop app opens it in the SYSTEM browser with // ?callback=http://127.0.0.1:/complete // On success the page POSTs the session token + identity to that localhost // callback. No secret key, no backend — the token is an identity marker for // the app's sign-in gate. // // Extra params from the app's native buttons: // strategy=oauth_google go straight to Google // strategy=email show the email form directly // switch=1 sign out of the browser session first, so // "Sign out" in the app re-arms the gate import { createAuthClient } from "better-auth/client"; const AUTH_URL = window.__BA_URL || window.location.origin; const authClient = createAuthClient({ baseURL: AUTH_URL, }); const msg = document.getElementById("msg"); const btnGoogle = document.getElementById("btn-google"); const btnEmail = document.getElementById("btn-email"); const emailForm = document.getElementById("email-form"); const emailInp = document.getElementById("email-inp"); const passInp = document.getElementById("pass-inp"); const authZone = document.getElementById("auth-zone"); function setMsg(text, isError) { if (!msg) return; msg.textContent = text; msg.className = isError ? "error" : ""; msg.style.display = "block"; } // Where to hand the token back. A ?callback is honored only if it points at // localhost (prevents this page being abused to exfiltrate a token elsewhere). function resolveCallback() { const raw = new URLSearchParams(location.search).get("callback"); if (!raw) return "/complete"; try { const u = new URL(raw); if (u.hostname === "127.0.0.1" || u.hostname === "localhost") return u.href; } catch (_e) { /* fall through */ } return "/complete"; } const callbackUrl = resolveCallback(); const qs = new URLSearchParams(location.search); const strategy = qs.get("strategy"); const wantsSwitch = qs.get("switch") === "1"; // The URL to return to after the OAuth provider redirects back. function returnUrl() { const u = new URL(location.href); u.searchParams.delete("strategy"); u.searchParams.delete("switch"); // Keep the callback param return u.href; } function showLoading(v) { btnGoogle.disabled = v; btnEmail.disabled = v; } let done = false; async function complete(session, user) { if (done) return; done = true; const token = session ? session.token : null; const email = user ? user.email : null; const name = user ? user.name : null; hideForm(); setMsg("Signed in. Returning to the app…", false); try { await fetch(callbackUrl, { method: "POST", headers: { "Content-Type": "text/plain" }, body: JSON.stringify({ token: token, userId: user ? user.id : null, email: email, name: name, }), }); setMsg("Signed in. You can close this tab — Cereal Milk is ready.", false); } catch (_e) { setMsg( "Signed in, but couldn't reach the app. Make sure Cereal Milk is open, then try again.", true ); } } function hideForm() { emailForm.style.display = "none"; btnGoogle.style.display = ""; btnEmail.style.display = ""; } function showEmailForm() { btnGoogle.style.display = "none"; btnEmail.style.display = "none"; emailForm.style.display = "flex"; emailInp.focus(); } // Sign in with email + password (falls back to sign-up if account doesn't exist) async function signInEmail() { const email = emailInp.value.trim(); const password = passInp.value; const name = email.split("@")[0]; if (!email || !password) { setMsg("Enter your email and password.", true); return; } setMsg("Signing in…", false); showLoading(true); // Try sign-in first let { data, error } = await authClient.signIn.email({ email, password }); // If sign-in fails, try sign-up if (error) { const signUpRes = await authClient.signUp.email({ email, password, name, }); if (signUpRes.error) { setMsg(signUpRes.error.message || "Sign-in failed.", true); showLoading(false); return; } // Sign-up succeeded (autoSignIn creates a session) } const sessionRes = await authClient.getSession(); if (sessionRes.data) { await complete(sessionRes.data.session, sessionRes.data.user); } } // Sign in with Google OAuth async function signInGoogle() { setMsg("Sending you to Google…", false); showLoading(true); try { await authClient.signIn.social({ provider: "google", callbackURL: returnUrl(), }); } catch (_e) { setMsg("Google sign-in couldn't start — try again or use email.", true); showLoading(false); } } // Check if we returned from Google OAuth async function checkReturnFromOAuth() { // Better Auth handles OAuth callbacks server-side, then redirects back // to this page. The session should be available. try { const { data: session, error } = await authClient.getSession(); if (error) return; if (session && session.session && session.user) { await complete(session.session, session.user); return true; } } catch (_e) { /* not signed in yet */ } return false; } // ---------- init ---------- btnGoogle.addEventListener("click", signInGoogle); btnEmail.addEventListener("click", showEmailForm); document.getElementById("email-back").addEventListener("click", hideForm); document.getElementById("email-submit").addEventListener("click", signInEmail); passInp.addEventListener("keydown", (e) => { if (e.key === "Enter") signInEmail(); }); // If the user already has a session (returning from OAuth or previous sign-in), // complete immediately. (async function boot() { // Handle sign-out switch: if the app requests a fresh sign-in, sign out the // existing browser session first. if (wantsSwitch) { try { const { data: session } = await authClient.getSession(); if (session) { setMsg("Signing out previous session…", false); await authClient.signOut(); } } catch (_e) { /* proceed to a fresh sign-in */ } } // Check for an existing session (e.g., returning from Google OAuth) const returned = await checkReturnFromOAuth(); if (returned) return; // Auto-trigger strategy if requested if (strategy === "oauth_google") { signInGoogle(); return; } if (strategy === "email") { showEmailForm(); return; } // Session check: if already signed in and no switch requested, complete if (!wantsSwitch) { const returned2 = await checkReturnFromOAuth(); if (returned2) return; } })();