phase 7: auth flow UI (email-code login + session resume + revocation)
Implements ui/PLAN.md Phase 7 end-to-end: - /login two-step form (email -> code) over the gateway public REST surface; /lobby placeholder issues the first authenticated user.account.get and renders the decoded display name. - SessionStore (Svelte 5 runes) with loading / unsupported / anonymous / authenticated states; layout-level route guard, browser-not-supported blocker, and a minimal SubscribeEvents revocation watcher that closes the active client within 1s on a clean stream end or Unauthenticated. - VITE_GATEWAY_BASE_URL + VITE_GATEWAY_RESPONSE_PUBLIC_KEY config plus AuthError taxonomy in api/auth.ts. - Vitest (auth-api, session-store, login-page) and Playwright e2e (auth-flow.spec.ts) on the four configured projects, with a fixture Ed25519 keypair forging Connect-Web JSON responses. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,70 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
import { goto } from "$app/navigation";
|
||||
import { page } from "$app/state";
|
||||
import { session } from "$lib/session-store.svelte";
|
||||
import { startRevocationWatcher } from "$lib/revocation-watcher";
|
||||
|
||||
let { children } = $props();
|
||||
|
||||
let stopWatcher: (() => void) | null = null;
|
||||
|
||||
onMount(() => {
|
||||
void session.init();
|
||||
return () => {
|
||||
if (stopWatcher !== null) {
|
||||
stopWatcher();
|
||||
stopWatcher = null;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
if (session.status === "authenticated" && stopWatcher === null) {
|
||||
stopWatcher = startRevocationWatcher();
|
||||
} else if (session.status !== "authenticated" && stopWatcher !== null) {
|
||||
stopWatcher();
|
||||
stopWatcher = null;
|
||||
}
|
||||
|
||||
const pathname = page.url.pathname;
|
||||
// Debug-only routes under /__debug/* run their own bootstrap
|
||||
// path against the storage primitives and must bypass the
|
||||
// auth guard so Phase 6's Playwright spec can drive the
|
||||
// keystore directly.
|
||||
if (pathname.startsWith("/__debug/")) {
|
||||
return;
|
||||
}
|
||||
if (session.status === "anonymous" && pathname !== "/login") {
|
||||
void goto("/login", { replaceState: true });
|
||||
} else if (session.status === "authenticated" && pathname === "/login") {
|
||||
void goto("/lobby", { replaceState: true });
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
{@render children()}
|
||||
{#if session.status === "loading"}
|
||||
<main class="status">
|
||||
<p>loading…</p>
|
||||
</main>
|
||||
{:else if session.status === "unsupported"}
|
||||
<main class="status">
|
||||
<h1>browser not supported</h1>
|
||||
<p>
|
||||
Galaxy requires Ed25519 in WebCrypto. The minimum supported browser
|
||||
versions are listed in the
|
||||
<a href="https://github.com/galaxy/galaxy/blob/main/ui/docs/storage.md"
|
||||
>storage topic doc</a
|
||||
>.
|
||||
</p>
|
||||
</main>
|
||||
{:else}
|
||||
{@render children()}
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.status {
|
||||
padding: 2rem;
|
||||
font-family: system-ui, sans-serif;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user