Self-hosted, federated location sharing app and server that prioritizes user privacy and security
end-to-end-encryption
location-sharing
privacy
self-hosted
federated
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}