Val Town is a social website to write and deploy JavaScript.
Build APIs and schedule functions from your browser.
Readme

Val Town Basic Auth

Add basic auth on top of any http val

Usage

Wrap your HTTP handler in the basicAuth middleware.

import { basicAuth } from "https://esm.town/v/pomdtr/basicAuth"; function handler(req: Request) { return new Response("You are authenticated!"); } export default basicAuth(handler);

To authenticate, paste an api token in the password prompt.

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
function extractToken(authorization) {
const parts = authorization.split(" ");
if (parts[0] == "Bearer") {
return parts[1];
}
if (parts[0] != "Basic") {
return "";
}
const plainAuth = atob(parts[1]);
const credentials = plainAuth.split(":");
// allow `curl <token>@xxx.web.val.run`
return credentials[1] || credentials[0];
}
type User = {
id: string;
};
async function fetchUser(token: string): Promise<User> {
const resp = await fetch("https://api.val.town/v1/me", {
headers: {
Authorization: `Bearer ${token}`,
},
});
if (resp.status !== 200) {
throw new Error("Could not fetch user");
}
return resp.json();
}
async function isTokenValid(token) {
try {
const [visitor, owner] = await Promise.all([fetchUser(token), fetchUser(Deno.env.get("valtown"))]);
return visitor.id == owner.id;
} catch (err) {
return false;
}
}
async function isRequestAuthenticated(req) {
if (!req.headers.has("authorization")) {
return false;
}
const token = extractToken(req.headers.get("authorization"));
return isTokenValid(token);
}
export function basicAuth(next: (Request) => Response | Promise<Response>) {
return async (req: Request) => {
if (req.headers.get("referer") == "https://www.val.town/") {
return new Response(
`Basic Auth is disabled in Val Town iframes.
<a href="/" target="blank_">Open in a new tab.</a>`,
{
status: 400,
headers: {
"Content-type": "text/html",
},
},
);
}
const isAuth = await isRequestAuthenticated(req);
if (!isAuth) {
return new Response("Unauthorized", {
status: 401,
headers: {
"WWW-Authenticate": "Basic",
},
});
}
return next(req);
};
}
December 18, 2023