From 9cc762c2e4e13cc90b30ca99d9f586bd6e2aa51a Mon Sep 17 00:00:00 2001 From: Sreeram Sreedhar Date: Mon, 22 Jun 2026 18:06:20 -0700 Subject: [PATCH] Add desktop browser auth --- apps/desktop/app/login/page.tsx | 90 ++++- apps/desktop/lib/auth.ts | 24 ++ apps/desktop/src-tauri/Cargo.lock | 530 +++++++++++++++++++++++++ apps/desktop/src-tauri/Cargo.toml | 4 + apps/desktop/src-tauri/src/auth.rs | 228 ++++++++++- apps/desktop/src-tauri/src/lib.rs | 51 ++- apps/desktop/src-tauri/tauri.conf.json | 7 + apps/web/app/(app)/page.tsx | 21 + apps/web/app/(auth)/login/page.tsx | 34 +- 9 files changed, 972 insertions(+), 17 deletions(-) diff --git a/apps/desktop/app/login/page.tsx b/apps/desktop/app/login/page.tsx index e38c630a..8c834f12 100644 --- a/apps/desktop/app/login/page.tsx +++ b/apps/desktop/app/login/page.tsx @@ -4,14 +4,61 @@ import { Button } from "@ui/components/button" import { Input } from "@ui/components/input" import { Label } from "@ui/components/label" import { useRouter } from "next/navigation" -import { type FormEvent, useState } from "react" -import { desktopDevAuthEnabled, storeToken } from "@/lib/auth" +import { type FormEvent, useEffect, useState } from "react" +import { + beginBrowserAuth, + desktopDevAuthEnabled, + getSession, + onAuthChanged, + onAuthError, + storeToken, +} from "@/lib/auth" export default function LoginPage() { const router = useRouter() const [token, setToken] = useState("") const [error, setError] = useState(null) const [isSubmitting, setIsSubmitting] = useState(false) + const [isBrowserAuthPending, setIsBrowserAuthPending] = useState(false) + + useEffect(() => { + let unlistenChanged: (() => void) | undefined + let unlistenError: (() => void) | undefined + + onAuthChanged(async (event) => { + if (!event.authenticated) return + setError(null) + setIsBrowserAuthPending(false) + try { + await getSession() + router.replace("/") + } catch (err) { + setError(formatError(err, "Could not validate browser sign-in")) + } + }) + .then((handler) => { + unlistenChanged = handler + }) + .catch(() => { + unlistenChanged = undefined + }) + + onAuthError((message) => { + setIsBrowserAuthPending(false) + setError(message) + }) + .then((handler) => { + unlistenError = handler + }) + .catch(() => { + unlistenError = undefined + }) + + return () => { + unlistenChanged?.() + unlistenError?.() + } + }, [router]) async function onSubmit(event: FormEvent) { event.preventDefault() @@ -34,6 +81,17 @@ export default function LoginPage() { } } + async function startBrowserAuth() { + setError(null) + setIsBrowserAuthPending(true) + try { + await beginBrowserAuth() + } catch (err) { + setError(formatError(err, "Could not open browser sign-in")) + setIsBrowserAuthPending(false) + } + } + return (
{/* Keep the frameless window draggable from the top edge. */} @@ -44,12 +102,23 @@ export default function LoginPage() {

Sign in to access your memories.

-

- Browser-based sign-in arrives in a later phase. + We'll open Supermemory in your browser and return here after + sign-in.

+ {error ? ( +

{error}

+ ) : null} {desktopDevAuthEnabled ? (
@@ -63,9 +132,6 @@ export default function LoginPage() { autoComplete="off" />
- {error ? ( -

{error}

- ) : null}