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

Lucia Middleware

Import users. Backed by Val Town SQLite.

Demo: @stevekrouse/lucia_middleware_demo

Initially built by @pomdtr.

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
// This approach uses the Lucia authentication library with a custom ValTownAdapter.
// It provides signup, login, and logout functionality with session management.
// The main tradeoff is the complexity of setting up Lucia, but it offers robust auth features.
/** @jsxImportSource https://esm.sh/react */
import { ValTownAdapter } from "https://esm.town/v/stevekrouse/lucia_adapter";
import { createUser, getUser, verifyPassword } from "https://esm.town/v/stevekrouse/lucia_sqlite";
import { Lucia, Session, User } from "npm:lucia@3.0.1";
import { renderToString } from "npm:react-dom/server";
import { CookieJar } from "https://deno.land/x/cookies/mod.ts";
const userTable = "user";
const sessionTable = "session";
const adapter = new ValTownAdapter({ userTable, sessionTable });
export const lucia = new Lucia(adapter, {
getUserAttributes: (attributes) => {
return {
username: attributes.username,
};
},
});
declare module "npm:lucia" {
interface Register {
Lucia: typeof lucia;
DatabaseUserAttributes: DatabaseUserAttributes;
}
}
interface DatabaseUserAttributes {
username: string;
}
// Helper function for redirects
function redirect(url: string, status = 302): Response {
return new Response(null, {
status,
headers: { Location: url }
});
}
export const luciaMiddleware = (handler: (req: Request) => Response | Promise<Response>) => {
return async (req: Request) => {
const url = new URL(req.url);
const cookies = new CookieJar(req);
const sessionId = cookies.get(lucia.sessionCookieName) ?? null;
let user: User | null = null;
let session: Session | null = null;
if (sessionId) {
const result = await lucia.validateSession(sessionId);
user = result.user;
session = result.session;
}
let response: Response;
if (url.pathname === "/auth/signup" && req.method === "GET") {
response = new Response(
renderToString(
<div>
<h1 style={{ textAlign: "center" }}>Sign up</h1>
<form
method="post"
action="/auth/signup"
style={{ display: "flex", flexDirection: "column", gap: "0.5rem", alignItems: "center" }}
>
<div>
<input id="username" name="username" autoComplete="off" placeholder="Username" />
</div>
<div>
<input id="password" name="password" type="password" autoComplete="off" placeholder="Password" />
</div>
<input type="submit" value="Sign up" />
</form>
</div>,
),
{ headers: { "Content-Type": "text/html" } },
);
} else if (url.pathname === "/auth/signup" && req.method === "POST") {
const formData = await req.formData();
const username = formData.get("username") as string;
const password = formData.get("password") as string;
if (
username.length < 3
|| username.length > 31
|| !/^[a-z0-9_-]+$/.test(username)
) {
return new Response("Invalid username: needs to be > 3 and < 31 characters and match regex: ^[a-z0-9_-]+$", {
status: 400,
});
}
if (password.length < 6 || password.length > 255) {
return new Response("Invalid password: needs to be > 6 and < 255 characters", {
status: 400,
});
stevekrouse-lucia_middleware_vanilla.web.val.run
August 10, 2024