Self-hosted, federated location sharing app and server that prioritizes user privacy and security
end-to-end-encryption location-sharing privacy self-hosted federated
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

at d75eb5eb44a18c427e2b2eacc98c2fa436544b49 82 lines 2.7 kB view raw
1import { Store } from "./store.ts"; 2 3function bufToBase64(buf: ArrayBuffer): string { 4 return btoa(String.fromCharCode(...new Uint8Array(buf))); 5} 6 7function strToBytes(str: string): Uint8Array { 8 return new TextEncoder().encode(str); 9} 10 11export async function createAccount(server_url: string, signup_key: string): Promise<{ user_id: string; is_admin: boolean }> { 12 try { 13 await Store.set("server_url", server_url); 14 const keyPair = await crypto.subtle.generateKey("Ed25519", true, ["sign", "verify"]); 15 const pubKeyRaw = await crypto.subtle.exportKey("raw", keyPair.publicKey); 16 const privKeyRaw = await crypto.subtle.exportKey("pkcs8", keyPair.privateKey); 17 const pub_key_b64 = bufToBase64(pubKeyRaw); 18 19 const response = await fetch(server_url + "/create-account", { 20 method: "POST", 21 headers: { "Content-Type": "application/json" }, 22 body: JSON.stringify({ signup_key, pub_key_b64 }), 23 }); 24 25 if (!response.ok) throw new Error(await response.text()); 26 const json = await response.json(); 27 28 await Store.set("user_id", json.user_id); 29 await Store.set("priv_key", bufToBase64(privKeyRaw)); 30 31 return json; 32 } catch (err) { 33 alert(`${err}`); 34 throw err; 35 } 36} 37 38export async function post(endpoint: string, data: object | string | undefined): Promise<any> { 39 try { 40 const user_id = await Store.get("user_id"); 41 const server_url = await Store.get("server_url"); 42 console.log(`Exhibit B: ${server_url}`); 43 const privKey_b64 = await Store.get("priv_key"); 44 45 if (!user_id || !privKey_b64) throw new Error("Missing user credentials"); 46 47 // Prepare request body bytes 48 let bodyStr = ""; 49 if (typeof data === "object") bodyStr = JSON.stringify(data); 50 else if (typeof data === "string") bodyStr = data; 51 const bodyBytes = strToBytes(bodyStr); 52 53 // Import private key and sign 54 const privKeyBytes = Uint8Array.from(atob(privKey_b64), (c) => c.charCodeAt(0)); 55 56 const privKey = await crypto.subtle.importKey("pkcs8", privKeyBytes.buffer, { name: "Ed25519" }, false, ["sign"]); 57 58 const signature = await crypto.subtle.sign("Ed25519", privKey, bodyBytes); 59 const signature_b64 = bufToBase64(signature); 60 61 // Encode header JSON to base64 62 const authJson = JSON.stringify({ user_id, signature: signature_b64 }); 63 const authHeader = btoa(authJson); 64 65 const headers: Record<string, string> = { 66 "x-auth": authHeader, 67 }; 68 if (typeof data === "object") headers["Content-Type"] = "application/json"; 69 70 const res = await fetch(`${server_url}/${endpoint}`, { 71 method: "POST", 72 headers, 73 body: bodyStr.length > 0 ? bodyStr : undefined, 74 }); 75 76 if (!res.ok) throw new Error(await res.text()); 77 return await res.text(); 78 } catch (err) { 79 alert(`${err}`); 80 throw err; 81 } 82}