Readme

Lucia Middleware

Import users. Backed by Val Town SQLite.

Demo: @stevekrouse/lucia_middleware_demo

If you want a version that is safe to import (but not call) on the frontend:

import { luciaMiddleware } from "https://esm.town/v/stevekrouse/lucia_middleware_safe";

Usage

  1. Wrap your HTTP handler in it, ie export default luciaMiddleware(handler)
  2. In your handler, redirect to /auth/signup, /auth/login, /auth/logout to trigger those flows. Remember Response.redirect is broken in Val Town right now, so either use links or return new Response(null, { 302, headers: { Location: "/place/to/redirect" }})
  3. In your HTTP handler, read the X-Lucia-Username header, ie const username = req.headers.get("X-Lucia-Username")
  4. If the user is logged in, you now have a username you can work with. If not, it will be empty

Custom Sign Up and Log In pages

By default, the middleware has very basic sign in and sign up pages. The only way to customize those right now is to fork this middleware and customize them to your liking.

Todos

  • Allow users to specify the users & sessions table
  • Remove Hono from this middleware (attempted in this fork: @stevekrouse/lucia_middleware_vanilla)
  • Allow users to customize the auth pages without forking the middleware

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
/** @jsxImportSource npm:hono/jsx **/
import { ValTownAdapter } from "https://esm.town/v/stevekrouse/lucia_adapter";
import { createUser, getUser, verifyPassword } from "https://esm.town/v/stevekrouse/lucia_sqlite";
import { Hono } from "npm:hono";
import { getCookie } from "npm:hono/cookie";
import { HTTPException } from "npm:hono/http-exception";
import { jsxRenderer } from "npm:hono/jsx-renderer";
import { Lucia, Session, User, verifyRequestOrigin } from "npm:lucia@3.0.1";
import { createDate, isWithinExpirationDate, TimeSpan } from "npm:oslo";
// TODO - make these configurable
const userTable = "lucia_users_1";
const sessionTable = "lucia_sessions_1";
const adapter = new ValTownAdapter({ userTable, sessionTable });
export const lucia = new Lucia(adapter, {
sessionCookie: {
attributes: {
sameSite: "none",
},
},
getUserAttributes: (attributes) => {
return {
username: attributes.username,
};
},
});
declare module "npm:lucia" {
interface Register {
Lucia: typeof lucia;
DatabaseUserAttributes: DatabaseUserAttributes;
}
}
interface DatabaseUserAttributes {
username: string;
}
export const luciaMiddleware = (handler: (req: Request) => Response | Promise<Response>) => {
const app = new Hono<{
Variables: {
user: User | null;
session: Session | null;
};
}>();
app.use("*", async (c, next) => {
const sessionId = getCookie(c, lucia.sessionCookieName) ?? null;
if (!sessionId) {
c.set("user", null);
c.set("session", null);
return next();
}
const { session, user } = await lucia.validateSession(sessionId);
if (session && session.fresh) {
c.header("Set-Cookie", lucia.createSessionCookie(session.id).serialize(), {
append: true,
});
}
if (!session) {
c.header("Set-Cookie", lucia.createBlankSessionCookie().serialize(), {
append: true,
});
}
c.set("user", user);
c.set("session", session);
return next();
});
app.get("/auth/signup", c =>
c.render(
<div>
<h1 style="text-align: center;">Sign up</h1>
<form
method="post"
action="/auth/signup"
style="display: flex; flex-direction: column; gap: 0.5rem; align-items: 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>,
));
app.post("/auth/signup", async c => {
const body = await c.req.parseBody();
if (typeof body.username !== "string" || typeof body.password !== "string") {
return new Response("Invalid login", {
status: 400,
});
}
const username = body.username;
const password = body.password;
if (
Val Town is a social website to write and deploy JavaScript.
Build APIs and schedule functions from your browser.
Comments
Nobody has commented on this val yet: be the first!
August 20, 2024