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:
@@ -0,0 +1,47 @@
|
||||
// Build-time configuration for the Galaxy gateway. Both values arrive
|
||||
// through Vite `import.meta.env` and resolve to module-level constants
|
||||
// at the first import.
|
||||
//
|
||||
// `VITE_GATEWAY_BASE_URL` is the base URL of the gateway public REST
|
||||
// surface and the Connect-Web authenticated edge (same host, same
|
||||
// port; the gateway listener serves both). It defaults to the local
|
||||
// dev address used by `tools/local-ci` and the integration suite.
|
||||
//
|
||||
// `VITE_GATEWAY_RESPONSE_PUBLIC_KEY` is the gateway's response-signing
|
||||
// Ed25519 public key, encoded as standard (non-URL-safe) base64 of
|
||||
// the raw 32-byte key. Decoded once on module load and exported as
|
||||
// `Uint8Array`. The value is only consumed by [GalaxyClient] when a
|
||||
// signed unary call is dispatched; the unauthenticated routes do not
|
||||
// need it. An empty or malformed value therefore does not block app
|
||||
// boot — it surfaces only when the lobby route opens its first
|
||||
// authenticated call.
|
||||
|
||||
const RAW_BASE_URL: string =
|
||||
(import.meta.env.VITE_GATEWAY_BASE_URL as string | undefined) ??
|
||||
"http://localhost:8080";
|
||||
|
||||
const RAW_RESPONSE_PUBLIC_KEY: string =
|
||||
(import.meta.env.VITE_GATEWAY_RESPONSE_PUBLIC_KEY as string | undefined) ??
|
||||
"";
|
||||
|
||||
export const GATEWAY_BASE_URL: string = stripTrailingSlash(RAW_BASE_URL);
|
||||
|
||||
export const GATEWAY_RESPONSE_PUBLIC_KEY: Uint8Array = decodeBase64(
|
||||
RAW_RESPONSE_PUBLIC_KEY,
|
||||
);
|
||||
|
||||
function stripTrailingSlash(url: string): string {
|
||||
return url.endsWith("/") ? url.slice(0, -1) : url;
|
||||
}
|
||||
|
||||
function decodeBase64(value: string): Uint8Array {
|
||||
if (value.length === 0) {
|
||||
return new Uint8Array();
|
||||
}
|
||||
const binary = atob(value);
|
||||
const bytes = new Uint8Array(binary.length);
|
||||
for (let i = 0; i < binary.length; i++) {
|
||||
bytes[i] = binary.charCodeAt(i);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
Reference in New Issue
Block a user