aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-02-16 16:05:56 -0800
committerAdam Soltys <[email protected]>2026-02-16 16:05:56 -0800
commit95427ee5189269e992ae4c788ce356709ed92c57 (patch)
tree675507f470d626a55ff530c8ff76cc4e8df424e0 /src
parentac06683100e089ad589661b14ebaf5dc9582a256 (diff)
downloadcoinos-ui-95427ee5189269e992ae4c788ce356709ed92c57.tar.gz
coinos-ui-95427ee5189269e992ae4c788ce356709ed92c57.zip
default accounts
Diffstat (limited to 'src')
-rw-r--r--src/components/Account.svelte9
-rw-r--r--src/routes/(app)/+layout.svelte63
-rw-r--r--src/routes/(app)/account/+server.ts11
-rw-r--r--src/routes/(app)/account/[id]/+page.svelte7
-rw-r--r--src/routes/(app)/account/ark/+page.svelte9
-rw-r--r--src/routes/(app)/account/pass/+page.svelte13
-rw-r--r--src/routes/(app)/account/seed/+page.svelte49
-rw-r--r--src/routes/(app)/post/[...path]/+server.ts10
-rw-r--r--src/routes/(app)/send/bitcoin/[address]/[amount]/[...feeRate]/+page.svelte15
-rw-r--r--src/routes/(static)/login/+page.svelte2
-rw-r--r--src/routes/(static)/register/+page.server.ts14
-rw-r--r--src/routes/(static)/register/+page.svelte76
12 files changed, 165 insertions, 113 deletions
diff --git a/src/components/Account.svelte b/src/components/Account.svelte
index 5ba1bedd..953e44ab 100644
--- a/src/components/Account.svelte
+++ b/src/components/Account.svelte
@@ -11,7 +11,6 @@
import { fiat } from "$lib/store";
import { arkkey, arkaid } from "$lib/ark";
import { getRememberedWalletPassword, forgetWalletPassword, getCachedPrfKey } from "$lib/passwordCache";
- import { isPrfEncrypted, prfDecrypt } from "$lib/crypto";
let { user, rates, account }: any = $props();
let currency = $derived(account.currency || user.currency);
@@ -31,14 +30,14 @@
let tryUnlockArk = async () => {
// Try PRF key first
const prfKey = getCachedPrfKey();
- if (prfKey && user.seed && isPrfEncrypted(user.seed)) {
+ if (prfKey) {
try {
const [{ HDKey }, { entropyToMnemonic, mnemonicToSeed }, { wordlist }] = await Promise.all([
import("@scure/bip32"),
import("@scure/bip39"),
import("@scure/bip39/wordlists/english.js"),
]);
- const entropy = await prfDecrypt(prfKey, user.seed);
+ const entropy = new Uint8Array(prfKey);
const mnemonic = entropyToMnemonic(entropy, wordlist);
const s = await mnemonicToSeed(mnemonic);
const master = HDKey.fromMasterSeed(s, versions);
@@ -130,7 +129,7 @@
let displayType = $derived(
accountType === "ark"
? $t("accounts.ark")
- : seed || (user.seed && fingerprint)
+ : seed || fingerprint
? $t("accounts.bitcoin")
: $t("accounts.custodial"),
);
@@ -174,7 +173,7 @@
<div class="flex items-center justify-center w-8 h-8 leading-[0]">
{#if isArk}
<img src="/images/ark.png" class="w-8 h-8 rounded-full object-cover" alt="Ark" />
- {:else if seed || (user.seed && fingerprint)}
+ {:else if seed || fingerprint}
<iconify-icon noobserver icon="cryptocurrency-color:btc" width="32"></iconify-icon>
{:else}
<img src="/images/icon.png" class="w-8 h-8" alt="Coinos" />
diff --git a/src/routes/(app)/+layout.svelte b/src/routes/(app)/+layout.svelte
index 732ac61e..6f2e061d 100644
--- a/src/routes/(app)/+layout.svelte
+++ b/src/routes/(app)/+layout.svelte
@@ -1,5 +1,5 @@
<script lang="ts">
- import { getWallet, syncTransactions, settle, refresh, arkkey, arkaid, sendArk, arkSending } from "$lib/ark";
+ import { getWallet, syncTransactions, settle, refresh, arkkey, arkaid, sendArk, arkSending, arkServerUrl } from "$lib/ark";
import { SvelteToast } from "@zerodevx/svelte-toast";
import { onDestroy, onMount } from "svelte";
import { close, connect, send, socket } from "$lib/socket";
@@ -22,9 +22,10 @@
import AppHeader from "$comp/AppHeader.svelte";
import Nostr from "$comp/Nostr.svelte";
import Password from "$comp/Password.svelte";
- import { s, f, toFiat, success, getCookie, warning, post } from "$lib/utils";
+ import { s, f, toFiat, success, getCookie, warning, post, versions } from "$lib/utils";
import { t, locale, loading } from "$lib/translations";
import { goto, invalidate, afterNavigate, preloadData } from "$app/navigation";
+ import { getCachedPrfKey } from "$lib/passwordCache";
let { data, children } = $props();
@@ -126,6 +127,62 @@
if (browser && user && $arkkey) initArk();
});
+ let autoProvisionAccounts = async () => {
+ if (!user) return;
+ if (sessionStorage.getItem("accounts:provisioned")) return;
+ const prfKey = getCachedPrfKey();
+ if (!prfKey) return;
+
+ sessionStorage.setItem("accounts:provisioned", "1");
+
+ try {
+ const res = await fetch("/account", { headers: { accept: "application/json" } });
+ const accounts = await res.json();
+
+ const hasBitcoin = accounts.some((a: any) => a.type === "bitcoin");
+ const hasArk = accounts.some((a: any) => a.type === "ark");
+ if (hasBitcoin && hasArk) return;
+
+ const [{ HDKey }, { entropyToMnemonic, mnemonicToSeed }, { wordlist }] = await Promise.all([
+ import("@scure/bip32"),
+ import("@scure/bip39"),
+ import("@scure/bip39/wordlists/english.js"),
+ ]);
+
+ const entropy = new Uint8Array(prfKey);
+ const mnemonic = entropyToMnemonic(entropy, wordlist);
+ const seed = await mnemonicToSeed(mnemonic);
+ const master = HDKey.fromMasterSeed(seed, versions);
+
+ if (!hasArk) {
+ const { SingleKey, Wallet } = await import("@arkade-os/sdk");
+ const { bytesToHex } = await import("@noble/hashes/utils.js");
+ const arkChild = master.derive("m/86'/0'/0'/0/0");
+ const arkHex = bytesToHex(arkChild.privateKey!);
+ const identity = SingleKey.fromHex(arkHex);
+ const wallet = await Wallet.create({ identity, arkServerUrl });
+ const arkAddress = await wallet.getAddress();
+ await post("/account", { name: "Savings", type: "ark", arkAddress });
+ }
+
+ if (!hasBitcoin) {
+ const child = master.derive("m/84'/0'/0'");
+ await post("/account", {
+ fingerprint: child.fingerprint.toString(16).padStart(8, "0"),
+ pubkey: child.publicExtendedKey,
+ name: "Vault",
+ type: "bitcoin",
+ accountIndex: 0,
+ });
+ }
+
+ invalidate("app:payments");
+ } catch (e) {
+ console.error("Auto-provision accounts failed:", e);
+ sessionStorage.removeItem("accounts:provisioned");
+ }
+ };
+
onMount(async () => {
if (browser) {
checkSocket();
@@ -138,6 +195,8 @@
$arkaid = "";
localStorage.removeItem("arkkey:uid");
}
+
+ autoProvisionAccounts();
}
// if (window.NDEFReader) {
diff --git a/src/routes/(app)/account/+server.ts b/src/routes/(app)/account/+server.ts
index fbe1214b..4387373e 100644
--- a/src/routes/(app)/account/+server.ts
+++ b/src/routes/(app)/account/+server.ts
@@ -1,6 +1,15 @@
-import { auth, post } from "$lib/utils";
+import { auth, get, post } from "$lib/utils";
import { error, json } from "@sveltejs/kit";
+export async function GET({ cookies }) {
+ try {
+ return json(await get("/accounts", auth(cookies)));
+ } catch (e: any) {
+ console.log(e);
+ error(500, e.message);
+ }
+}
+
export async function POST({ cookies, request }) {
try {
const body = await request.json();
diff --git a/src/routes/(app)/account/[id]/+page.svelte b/src/routes/(app)/account/[id]/+page.svelte
index cfcb6dea..0bab9c1b 100644
--- a/src/routes/(app)/account/[id]/+page.svelte
+++ b/src/routes/(app)/account/[id]/+page.svelte
@@ -12,14 +12,13 @@
import { entropyToMnemonic, mnemonicToSeed } from "@scure/bip39";
import { wordlist } from "@scure/bip39/wordlists/english.js";
import { getRememberedWalletPassword, forgetWalletPassword, getCachedPrfKey } from "$lib/passwordCache";
- import { isPrfEncrypted, prfDecrypt } from "$lib/crypto";
let { data } = $props();
let account = $derived(data.account);
let user = $derived(data.user);
let rates = $derived(data.rates);
let id = $derived(account.id);
- let seed = $derived(account.seed || (account.fingerprint && user.seed));
+ let seed = $derived(account.seed || account.fingerprint);
let displayType = $derived(
account.type === "ark"
? $t("accounts.ark")
@@ -73,9 +72,9 @@
let revealWithPrfKey = async () => {
const prfKey = getCachedPrfKey();
- if (!prfKey || !seed || !isPrfEncrypted(seed)) return false;
+ if (!prfKey) return false;
try {
- const entropy = await prfDecrypt(prfKey, seed);
+ const entropy = new Uint8Array(prfKey);
mnemonic = entropyToMnemonic(entropy, wordlist);
return true;
} catch (e) {
diff --git a/src/routes/(app)/account/ark/+page.svelte b/src/routes/(app)/account/ark/+page.svelte
index 7725f81a..f2bc8991 100644
--- a/src/routes/(app)/account/ark/+page.svelte
+++ b/src/routes/(app)/account/ark/+page.svelte
@@ -20,14 +20,13 @@
getCachedPrfKey,
rememberPrfKey,
} from "$lib/passwordCache";
- import { isPrfEncrypted, prfDecrypt } from "$lib/crypto";
import { loginWithPasskey } from "$lib/passkey";
let { data } = $props();
let user = $derived(data.user);
let hasMasterSeed = $derived(!!user.seed);
- let hasPrfSeed = $derived(!!user.seed && isPrfEncrypted(user.seed));
+ let hasPrfSeed = $derived(!!getCachedPrfKey());
let privateKey = $state("");
let arkAddress = $state("");
@@ -43,13 +42,13 @@
let revealConfirm = $state(false);
let rememberForMs = $state(defaultRememberForMs);
- let name = "Ark Vault";
+ let name = "Savings";
let type = "ark";
let deriveFromPrfKey = async (prfKey: ArrayBuffer) => {
try {
const { SingleKey, Wallet } = await import("@arkade-os/sdk");
- const entropy = await prfDecrypt(prfKey, user.seed);
+ const entropy = new Uint8Array(prfKey);
const mnemonic = entropyToMnemonic(entropy, wordlist);
const seed = await mnemonicToSeed(mnemonic);
const master = HDKey.fromMasterSeed(seed, versions);
@@ -225,7 +224,7 @@
<div class="space-y-5">
<div class="flex items-center justify-center gap-2">
- <h1 class="text-3xl font-semibold">Create Ark Wallet</h1>
+ <h1 class="text-3xl font-semibold">Create Savings Account</h1>
</div>
<div class="container w-full mx-auto text-lg px-4 max-w-xl space-y-5">
diff --git a/src/routes/(app)/account/pass/+page.svelte b/src/routes/(app)/account/pass/+page.svelte
index ba1e878c..23e54ac7 100644
--- a/src/routes/(app)/account/pass/+page.svelte
+++ b/src/routes/(app)/account/pass/+page.svelte
@@ -17,7 +17,6 @@
forgetWalletPassword,
getCachedPrfKey,
} from "$lib/passwordCache";
- import { isPrfEncrypted, prfEncrypt } from "$lib/crypto";
let { data }: any = $props();
@@ -36,18 +35,16 @@
return;
}
- // PRF users: encrypt seed and create account immediately, skip password
+ // PRF users: derive from PRF key directly, skip password
const prfKey = getCachedPrfKey();
if (prfKey) {
submitting = true;
await tick();
try {
- const entropy = mnemonicToEntropy($mnemonic as string, wordlist);
- const encryptedSeed = await prfEncrypt(prfKey, entropy);
- await post("/post/user", { seed: encryptedSeed });
-
+ const entropy = new Uint8Array(prfKey);
+ const mn = entropyToMnemonic(entropy, wordlist);
const master = HDKey.fromMasterSeed(
- await mnemonicToSeed($mnemonic as string),
+ await mnemonicToSeed(mn),
versions,
);
const child = master.derive("m/84'/0'/0'");
@@ -56,7 +53,7 @@
await post("/account", {
fingerprint,
pubkey,
- name: $t("accounts.savings"),
+ name: "Vault",
type: "bitcoin",
accountIndex: 0,
});
diff --git a/src/routes/(app)/account/seed/+page.svelte b/src/routes/(app)/account/seed/+page.svelte
index 994a4422..adfd5573 100644
--- a/src/routes/(app)/account/seed/+page.svelte
+++ b/src/routes/(app)/account/seed/+page.svelte
@@ -9,7 +9,6 @@
import { wordlist } from "@scure/bip39/wordlists/english.js";
import { HDKey } from "@scure/bip32";
import { goto } from "$app/navigation";
- import { isPrfEncrypted, prfDecrypt } from "$lib/crypto";
import { getCachedPrfKey } from "$lib/passwordCache";
import Spinner from "$comp/Spinner.svelte";
@@ -19,33 +18,29 @@
onMount(async () => {
if (!browser) return;
- if (user.seed && isPrfEncrypted(user.seed)) {
- const prfKey = getCachedPrfKey();
- if (prfKey) {
- creating = true;
- try {
- const entropy = await prfDecrypt(prfKey, user.seed);
- const mn = entropyToMnemonic(entropy, wordlist);
- const seed = await mnemonicToSeed(mn);
- const master = HDKey.fromMasterSeed(seed, versions);
- const child = master.derive("m/84'/0'/0'");
- const pubkey = child.publicExtendedKey;
- const fingerprint = child.fingerprint.toString(16).padStart(8, "0");
- await post("/account", {
- fingerprint,
- pubkey,
- name: $t("accounts.savings"),
- type: "bitcoin",
- accountIndex: 0,
- });
- goto(`/${user.username}`);
- } catch (e: any) {
- console.log(e);
- fail(e.message);
- creating = false;
- }
- } else {
+ const prfKey = getCachedPrfKey();
+ if (prfKey) {
+ creating = true;
+ try {
+ const entropy = new Uint8Array(prfKey);
+ const mn = entropyToMnemonic(entropy, wordlist);
+ const seed = await mnemonicToSeed(mn);
+ const master = HDKey.fromMasterSeed(seed, versions);
+ const child = master.derive("m/84'/0'/0'");
+ const pubkey = child.publicExtendedKey;
+ const fingerprint = child.fingerprint.toString(16).padStart(8, "0");
+ await post("/account", {
+ fingerprint,
+ pubkey,
+ name: "Vault",
+ type: "bitcoin",
+ accountIndex: 0,
+ });
goto(`/${user.username}`);
+ } catch (e: any) {
+ console.log(e);
+ fail(e.message);
+ creating = false;
}
return;
}
diff --git a/src/routes/(app)/post/[...path]/+server.ts b/src/routes/(app)/post/[...path]/+server.ts
index 244683ee..2c3e6d18 100644
--- a/src/routes/(app)/post/[...path]/+server.ts
+++ b/src/routes/(app)/post/[...path]/+server.ts
@@ -3,10 +3,18 @@ import { auth } from "$lib/utils";
export async function POST({ cookies, fetch, params, request }) {
const { path } = params;
+ const headers: Record<string, string> = { "Content-Type": "application/json" };
+
+ const authHeader = request.headers.get("authorization");
+ if (authHeader) {
+ headers.authorization = authHeader;
+ } else {
+ Object.assign(headers, auth(cookies));
+ }
return fetch(`${base}/${path}`, {
method: "POST",
body: JSON.stringify(await request.json()),
- headers: { "Content-Type": "application/json", ...auth(cookies) },
+ headers,
});
}
diff --git a/src/routes/(app)/send/bitcoin/[address]/[amount]/[...feeRate]/+page.svelte b/src/routes/(app)/send/bitcoin/[address]/[amount]/[...feeRate]/+page.svelte
index b62d2ec9..e14d41ba 100644
--- a/src/routes/(app)/send/bitcoin/[address]/[amount]/[...feeRate]/+page.svelte
+++ b/src/routes/(app)/send/bitcoin/[address]/[amount]/[...feeRate]/+page.svelte
@@ -14,7 +14,6 @@
import { hex as hexUtil } from "@scure/base";
import * as btc from "@scure/btc-signer";
import { getRememberedWalletPassword, forgetWalletPassword, getCachedPrfKey } from "$lib/passwordCache";
- import { isPrfEncrypted, prfDecrypt } from "$lib/crypto";
import { sendArkViaForward } from "$lib/ark";
import Amount from "$comp/Amount.svelte";
@@ -30,12 +29,10 @@
let error = $state("");
let trySignWithCachedPassword = async () => {
- let seed = account.seed || data.user.seed;
- if (seed && isPrfEncrypted(seed)) {
- const prfKey = getCachedPrfKey();
- if (!prfKey) return false;
+ const prfKey = getCachedPrfKey();
+ if (prfKey) {
try {
- await signTxWithPrf(prfKey, seed);
+ await signTxWithPrf(prfKey);
return true;
} catch (e: any) {
return false;
@@ -86,7 +83,7 @@
return;
}
- if ((account.seed || (data.user.seed && account.fingerprint)) && !signed) (cancel(), void togglePassword());
+ if ((account.seed || account.fingerprint) && !signed) (cancel(), void togglePassword());
return async ({ result }) => {
if (result.type === "redirect") {
goto(result.location);
@@ -97,7 +94,7 @@
};
};
- let signTxWithPrf = async (prfKey: ArrayBuffer, encryptedSeed: string) => {
+ let signTxWithPrf = async (prfKey: ArrayBuffer) => {
const [{ Transaction }, { HDKey }, { entropyToMnemonic, mnemonicToSeed }, { wordlist }] =
await Promise.all([
import("@scure/btc-signer"),
@@ -105,7 +102,7 @@
import("@scure/bip39"),
import("@scure/bip39/wordlists/english.js"),
]);
- let entropy = await prfDecrypt(prfKey, encryptedSeed);
+ let entropy = new Uint8Array(prfKey);
let mnemonic = entropyToMnemonic(entropy, wordlist);
let seed = await mnemonicToSeed(mnemonic);
let master = HDKey.fromMasterSeed(seed, network as any);
diff --git a/src/routes/(static)/login/+page.svelte b/src/routes/(static)/login/+page.svelte
index 1859af6f..fc1a6921 100644
--- a/src/routes/(static)/login/+page.svelte
+++ b/src/routes/(static)/login/+page.svelte
@@ -126,7 +126,7 @@
const result = deserialize(await response.text());
- if (result.type === "success") {
+ if (result.type === "success" || result.type === "redirect") {
rememberPrfKey(prfKey, defaultRememberForMs);
await invalidateAll();
}
diff --git a/src/routes/(static)/register/+page.server.ts b/src/routes/(static)/register/+page.server.ts
index e34bcbae..6986e890 100644
--- a/src/routes/(static)/register/+page.server.ts
+++ b/src/routes/(static)/register/+page.server.ts
@@ -15,9 +15,9 @@ export const actions = {
register: async ({ request }) => {
const ip = request.headers.get("cf-connecting-ip");
const form = await fd(request);
- const { picture, username, challenge, recaptcha, pubkey } = form;
+ const { picture, username, challenge, recaptcha } = form;
- const user = { picture, username, challenge, recaptcha, pubkey };
+ const user = { picture, username, challenge, recaptcha };
const headers: Record<string, string> = {};
if (ip) headers["cf-connecting-ip"] = ip;
@@ -32,7 +32,7 @@ export const actions = {
activate: async ({ cookies, request }) => {
const form = await fd(request);
- const { token, username, sk } = form;
+ const { token, username, sk, pubkey } = form;
let { loginRedirect } = form;
if (loginRedirect === "undefined") loginRedirect = undefined;
@@ -55,6 +55,14 @@ export const actions = {
});
}
+ if (pubkey) {
+ try {
+ await post("/user", { pubkey }, { authorization: `Bearer ${token}` });
+ } catch (e) {
+ console.log("Failed to update pubkey:", e);
+ }
+ }
+
redirect(303, loginRedirect || `/${username}`);
},
};
diff --git a/src/routes/(static)/register/+page.svelte b/src/routes/(static)/register/+page.svelte
index 0652b1a8..273d319a 100644
--- a/src/routes/(static)/register/+page.svelte
+++ b/src/routes/(static)/register/+page.svelte
@@ -15,7 +15,6 @@
import { focus, fail, versions, post } from "$lib/utils";
import { registerPasskey } from "$lib/passkey";
- import { prfEncrypt, isPrfEncrypted } from "$lib/crypto";
import { rememberPrfKey, defaultRememberForMs } from "$lib/passwordCache";
import { avatar, signer, password, pin, loginRedirect } from "$lib/store";
import { t } from "$lib/translations";
@@ -53,11 +52,7 @@
let needsPasskey = $state(false);
// Stored between handleSubmit and retryPasskey
- let pendingMnemonic: string | undefined;
- let pendingSkHex: string | undefined;
let pendingToken: string | undefined;
- let pendingSk: string | undefined;
- let pendingLoginRedirect: string | undefined;
let cleared;
let clear = () => {
@@ -130,31 +125,6 @@
return;
}
- // Generate mnemonic and derive Nostr pubkey (NIP-06)
- const [
- { generateMnemonic, mnemonicToSeed, mnemonicToEntropy },
- { wordlist },
- { HDKey },
- { getPublicKey },
- { bytesToHex },
- ] = await Promise.all([
- import("@scure/bip39"),
- import("@scure/bip39/wordlists/english.js"),
- import("@scure/bip32"),
- import("nostr-tools"),
- import("@noble/hashes/utils.js"),
- ]);
-
- const mnemonic = await generateMnemonic(wordlist);
- pendingMnemonic = mnemonic;
- const seed = await mnemonicToSeed(mnemonic);
- const master = HDKey.fromMasterSeed(seed, versions);
- const nostrChild = master.derive("m/44'/1237'/0'/0/0");
- const sk = nostrChild.privateKey!;
- const pubkey = getPublicKey(sk);
- pendingSkHex = bytesToHex(sk);
- data.set("pubkey", pubkey);
-
data.set("picture", `${$page.url.origin}/api/public/${punks[index]}.webp`);
if ($avatar) {
try {
@@ -186,11 +156,8 @@
if (result.type === "success") {
// User created but no cookies set yet — must complete passkey first
- const { token, sk: serverSk } = (result as any).data;
+ const { token } = (result as any).data;
pendingToken = token;
- pendingSk = serverSk;
- pendingLoginRedirect =
- data.get("loginRedirect") as string || `/${username}`;
let prfKey: ArrayBuffer;
try {
@@ -211,35 +178,50 @@
}
async function activateSession(prfKey: ArrayBuffer) {
- if (!pendingMnemonic) return;
-
- const { mnemonicToEntropy } = await import("@scure/bip39");
- const { wordlist } = await import("@scure/bip39/wordlists/english.js");
- const entropy = mnemonicToEntropy(pendingMnemonic, wordlist);
- const encryptedSeed = await prfEncrypt(prfKey, entropy);
rememberPrfKey(prfKey, defaultRememberForMs);
- // Activate session — sets auth cookies server-side
+ // Derive Nostr key from PRF key
+ const [
+ { entropyToMnemonic, mnemonicToSeed },
+ { wordlist },
+ { HDKey },
+ { getPublicKey },
+ { bytesToHex },
+ ] = await Promise.all([
+ import("@scure/bip39"),
+ import("@scure/bip39/wordlists/english.js"),
+ import("@scure/bip32"),
+ import("nostr-tools"),
+ import("@noble/hashes/utils.js"),
+ ]);
+
+ const entropy = new Uint8Array(prfKey);
+ const mnemonic = entropyToMnemonic(entropy, wordlist);
+ const seed = await mnemonicToSeed(mnemonic);
+ const master = HDKey.fromMasterSeed(seed, versions);
+ const nostrChild = master.derive("m/44'/1237'/0'/0/0");
+ const sk = nostrChild.privateKey!;
+ const pubkey = getPublicKey(sk);
+ const skHex = bytesToHex(sk);
+
+ // Activate session — sets auth cookies and updates pubkey server-side
const activateData = new FormData();
activateData.set("token", pendingToken!);
activateData.set("username", username!);
- if (pendingSk) activateData.set("sk", pendingSkHex || pendingSk);
+ activateData.set("sk", skHex);
+ activateData.set("pubkey", pubkey);
const activateResponse = await fetch("/register?/activate", {
method: "POST",
body: activateData,
});
- // Cookies are now set — save the encrypted seed before navigating
- await post("/post/user", { seed: encryptedSeed });
-
+ const activateResult = deserialize(await activateResponse.text());
await invalidateAll();
- const activateResult = deserialize(await activateResponse.text());
if (activateResult.type === "redirect") {
applyAction(activateResult);
} else {
- // Fallback: navigate manually
applyAction({ type: "redirect", status: 303, location: `/${username}` });
}
}