aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-02-27 15:24:13 -0800
committerAdam Soltys <[email protected]>2026-02-27 15:24:13 -0800
commit16da2cb00d16f87d02535bfdae815f83f19f251f (patch)
tree2e6d003975d5a7a67cc81243da4a59bc476fff48 /src
parent11fbf552ebbb9d1d5ae6823893ee80ae23f450a0 (diff)
downloadcoinos-ui-16da2cb00d16f87d02535bfdae815f83f19f251f.tar.gz
coinos-ui-16da2cb00d16f87d02535bfdae815f83f19f251f.zip
fix race condition
Diffstat (limited to 'src')
-rw-r--r--src/components/Account.svelte24
-rw-r--r--src/routes/(static)/login/+page.server.ts51
-rw-r--r--src/routes/(static)/register/auth/+page.svelte4
3 files changed, 15 insertions, 64 deletions
diff --git a/src/components/Account.svelte b/src/components/Account.svelte
index 45ffdd8b..ff9218fe 100644
--- a/src/components/Account.svelte
+++ b/src/components/Account.svelte
@@ -18,8 +18,8 @@
let arkBalance = $state(0);
let arkLoading = $state(false);
- let tryUnlockArk = async () => {
- const deriveArkKey = async (key: ArrayBuffer) => {
+ let tryUnlockArk = async (): Promise<string | false> => {
+ const deriveArkKey = async (key: ArrayBuffer): Promise<string> => {
const [{ HDKey }, { entropyToMnemonic, mnemonicToSeed }, { wordlist }] = await Promise.all([
import("@scure/bip32"),
import("@scure/bip39"),
@@ -36,8 +36,7 @@
const s = await mnemonicToSeed(mnemonicStr);
const master = HDKey.fromMasterSeed(s, versions);
const arkChild = master.derive("m/86'/0'/0'/0/0");
- $arkkey = bytesToHex(arkChild.privateKey!);
- $arkaid = id;
+ return bytesToHex(arkChild.privateKey!);
};
// Try cached PRF key or silent nsec
@@ -45,8 +44,7 @@
const entropy = await getWalletEntropy();
if (entropy) {
try {
- await deriveArkKey(entropy);
- return true;
+ return await deriveArkKey(entropy);
} catch (e) {
console.log("Wallet entropy unlock failed", e);
}
@@ -55,8 +53,7 @@
// Try interactive nostr derivation
try {
const interactiveEntropy = await deriveNostrEntropy();
- await deriveArkKey(interactiveEntropy);
- return true;
+ return await deriveArkKey(interactiveEntropy);
} catch (e) {
console.log("Interactive nostr unlock failed", e);
return false;
@@ -69,9 +66,14 @@
document.cookie = `aid=${id}; path=/; max-age=86400`;
if (isArk) {
if ($arkkey && $arkaid === id) {
- goto(url);
- } else if (await tryUnlockArk()) {
- goto(url);
+ await goto(url);
+ } else {
+ const key = await tryUnlockArk();
+ if (key) {
+ await goto(url);
+ $arkkey = key;
+ $arkaid = id;
+ }
}
} else {
$arkkey = undefined;
diff --git a/src/routes/(static)/login/+page.server.ts b/src/routes/(static)/login/+page.server.ts
index d402fdad..68143d83 100644
--- a/src/routes/(static)/login/+page.server.ts
+++ b/src/routes/(static)/login/+page.server.ts
@@ -97,57 +97,6 @@ export const actions = {
};
},
- passwordAuth: async ({ cookies, fetch, request }) => {
- const form = await fd(request);
- let { event, challenge, username, loginRedirect, recaptcha, token: twofa } = form;
- if (loginRedirect === "null" || loginRedirect === "undefined") loginRedirect = undefined;
- event = JSON.parse(event);
-
- const maxAge = 380 * 24 * 60 * 60;
-
- const res = await fetch(`${PUBLIC_COINOS_URL}/authKeyLogin`, {
- method: "POST",
- body: JSON.stringify({ event, challenge, username, recaptcha, twofa }),
- headers: {
- "content-type": "application/json",
- accept: "application/json",
- "cf-connecting-ip": request.headers.get("cf-connecting-ip") ?? "",
- "x-forwarded-host": request.headers.get("host") ?? "",
- },
- });
-
- const text = await res.text();
-
- if (res.status === 401) {
- if (text.startsWith("2fa")) throw new Error("2fa");
- throw new Error(text);
- }
-
- if (!res.ok) {
- let message;
- try {
- ({ message } = JSON.parse(text));
- } catch {
- message = text;
- }
- return fail(400, { error: message || "Login failed" });
- }
-
- const { user, token } = JSON.parse(text);
- if (!token) return fail(400, { error: "Login failed" });
- const { username: uname, language } = user;
-
- const expires = new Date();
- expires.setSeconds(expires.getSeconds() + maxAge);
-
- const opts = { path: "/", expires };
- if (language) cookies.set("lang", language, opts);
- cookies.set("username", uname, opts);
- cookies.set("token", token, opts);
-
- redirect(307, loginRedirect || `/${uname}`);
- },
-
passkey: async ({ cookies, fetch, request }) => {
const form = await fd(request);
let { credential, challengeId, loginRedirect } = form;
diff --git a/src/routes/(static)/register/auth/+page.svelte b/src/routes/(static)/register/auth/+page.svelte
index d285e0af..ad5d6e54 100644
--- a/src/routes/(static)/register/auth/+page.svelte
+++ b/src/routes/(static)/register/auth/+page.svelte
@@ -71,7 +71,7 @@
const getRecaptchaToken = () =>
new Promise((resolve, reject) => {
- if (isTor) return resolve("");
+ if (isTor || !recaptchaSiteKey) return resolve("");
if (!browser || !grecaptcha) return reject(new Error("captcha unavailable"));
grecaptcha.ready(() => {
grecaptcha.execute(recaptchaSiteKey, { action: "register" }).then(resolve).catch(reject);
@@ -144,7 +144,7 @@
const result = deserialize(await response.text());
- if (result.type === "success") {
+ if (result.type === "success" || result.type === "redirect") {
try {
const { deriveAuthKeypair } = await import("$lib/deriveAuthKey");
const { sk } = await deriveAuthKeypair(user.username, user.password);