Public
HTTP (deprecated)
Val Town is a social website to write and deploy JavaScript.
Build APIs and schedule functions from your browser.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/** @jsxImportSource https://esm.sh/preact */
import { blob } from "https://esm.town/v/std/blob";
// import * as opaque from "npm:@cloudflare/opaque-ts";
import { Hono } from "npm:hono@3";
import { render } from "npm:preact-render-to-string";
const resp = (content, status) =>
new Response(render(content), {
status,
headers: {
"Content-Type": "text/html",
},
});
const trimSuffix = (s: string, suffix: string): string => {
if (!s.endsWith(suffix)) {
return s;
}
return s.slice(0, s.length - suffix.length);
};
const getPassword = async (): Promise<string | undefined> => {
try {
let resp = await blob.get(PASSWORD_BLOB_KEY);
return await resp.text();
} catch (e) {
return undefined;
}
};
const KNOWN_HOSTNAME = ".web.val.run";
const PASSWORD_BLOB_KEY = "password"; // hah
const app = new Hono();
app.get("/", async (c) => {
const password = await getPassword();
const u = new URL(c.req.url);
if (!u.host.endsWith(KNOWN_HOSTNAME)) {
return resp((<div>Hostname does not end with {KNOWN_HOSTNAME}, cannot continue</div>), 400);
}
const userAndValName = trimSuffix(u.host, KNOWN_HOSTNAME);
const [username, valName] = userAndValName.split("-");
if (username === "") {
return resp((<div>Username not found</div>), 400);
}
if (valName !== "userspaceauth") {
return resp((<div>Val must be called "userspaceAuth"</div>), 400);
}
let content = password
? <div>Your password has been set up and this val is ready to authenticate requests for you</div>
: (
<form method="post">
<p>
Enter a password that you'll use to authenticate:
</p>
<div>
<input type="password" name="password"></input>
<button type="submit">Submit</button>
</div>
</form>
);
return resp(
(<>
<div>Username: {username}</div>
<div>Val Name: {valName}</div>
<div>{content}</div>
</>),
200,
);
});
app.post("/", async (c) => {
const fd = await c.req.formData();
const password = fd.get("password");
// TODO: more validation
if (typeof password !== "string" || password.length < 8) {
return resp((<div>Invalid password!</div>), 400);
}
await blob.set(PASSWORD_BLOB_KEY, "password");
return c.redirect("/");
});
app.get("/reset", async (c) => {
// TODO: this can be abused
await blob.delete(PASSWORD_BLOB_KEY);
});
app.post("/validate", async (c) => {
const realPassword = await getPassword();
const fd = await c.req.formData();
const password = fd.get("password");
const match = password === realPassword;
return c.json(match, match ? 200 : 401);
});
app.get("/validate/:password", async (c) => {
const realPassword = await getPassword();
const password = c.req.param("password");
const match = password === realPassword;
return c.json(match, match ? 200 : 401);
});
maxm-userspaceauth.web.val.run
April 7, 2024