Self-hosted, federated location sharing app and server that prioritizes user privacy and security
end-to-end-encryption location-sharing privacy self-hosted federated
at main 88 lines 2.5 kB view raw
1import { expect } from "bun:test"; 2 3export const URL = "http://127.0.0.1:3000"; 4 5// Generate an Ed25519 keypair and register it with the server. 6export async function generateUser( 7 signup_key: string | undefined, 8 should_be_admin: boolean = false, 9): Promise<{ user_id: string; pubKey: Uint8Array; privKey: Uint8Array }> { 10 if (!signup_key) { 11 throw new Error("signup_key was not provided or captured from server output"); 12 } 13 14 // Create Ed25519 keypair 15 const keyPair = await crypto.subtle.generateKey({ name: "Ed25519" }, true, ["sign", "verify"]); 16 17 // Export raw public key 18 const pubKey = new Uint8Array(await crypto.subtle.exportKey("raw", keyPair.publicKey)); 19 const privKey = new Uint8Array(await crypto.subtle.exportKey("pkcs8", keyPair.privateKey)); 20 21 // Base64 encode the public key 22 const pub_key_b64 = btoa(String.fromCharCode(...pubKey)); 23 24 // Send signup_key and pubkey to server 25 const res = await fetch(`${URL}/create-account`, { 26 method: "POST", 27 headers: { "Content-Type": "application/json" }, 28 body: JSON.stringify({ signup_key, pub_key_b64 }), 29 }); 30 31 expect(res.status).toBe(200); 32 const json = await res.json(); 33 expect(json).toEqual({ 34 user_id: expect.any(String), 35 is_admin: should_be_admin, 36 }); 37 38 return { user_id: json.user_id, pubKey, privKey }; 39} 40 41export async function post( 42 endpoint: string, 43 user: { user_id: string; privKey: Uint8Array }, 44 data: Object | string | undefined, 45): Promise<any> { 46 let bodyBytes: Uint8Array; 47 48 if (typeof data === "object") { 49 const json = JSON.stringify(data); 50 bodyBytes = new TextEncoder().encode(json); 51 } else if (typeof data === "string") { 52 bodyBytes = new TextEncoder().encode(data); 53 } else { 54 bodyBytes = new Uint8Array(); 55 } 56 57 // Sign body using private key 58 const privateKey = await crypto.subtle.importKey( 59 "pkcs8", 60 user.privKey.buffer, 61 { name: "Ed25519" }, 62 false, 63 ["sign"], 64 ); 65 66 const signature = new Uint8Array(await crypto.subtle.sign("Ed25519", privateKey, bodyBytes)); 67 const signature_b64 = btoa(String.fromCharCode(...signature)); 68 69 const authJson = JSON.stringify({ 70 user_id: user.user_id, 71 signature: signature_b64, // changed key 72 }); 73 const authHeader = btoa(authJson); 74 75 const headers: Record<string, string> = { 76 "x-auth": authHeader, 77 "Content-Type": "application/json", 78 }; 79 80 const res = await fetch(`${URL}/${endpoint}`, { 81 method: "POST", 82 headers, 83 body: bodyBytes.length > 0 ? new TextDecoder().decode(bodyBytes) : undefined, 84 }); 85 86 expect(res.status).toBe(200); 87 return await res.text(); 88}