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 { thisWebURL } from "https://esm.town/v/stevekrouse/thisWebURL?v=2";
import { ValTownAdapter } from "https://esm.town/v/yawnxyz/lucia_adapter_valtown";
import { createUser, getUser, verifyPassword } from "https://esm.town/v/yawnxyz/lucia_sqlite";
import { Hono } from "npm:hono";
import { getCookie, setCookie } 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 });
// adding Github + Google Oauth support
import { generateId } from "npm:lucia@3.0.1"; // somehow generateIdFromEntropySize doesn't exist in npm:lucia
import { generateState, OAuth2RequestError, generateCodeVerifier, GitHub, Google } from "npm:arctic";
import { serializeCookie, parseCookies } from "npm:oslo/cookie";
const githubClientId = Deno.env.get("GITHUB_OAUTH_COVERSHEET_CLIENT");
const githubClientSecret = Deno.env.get("GITHUB_OAUTH_COVERSHEET_SECRET");
export const github = new GitHub(githubClientId, githubClientSecret);
const googleClientId = Deno.env.get("GOOGLE_OAUTH_COVERSHEET_CLIENT");
const googleClientSecret = Deno.env.get("GOOGLE_OAUTH_COVERSHEET_SECRET");
const google = new Google(googleClientId, googleClientSecret, `https://yawnxyz-hncloneluciaoauth.web.val.run/auth/google/callback`);
// TODO: need to be able to set these dynamically from the requester app
export const lucia = new Lucia(adapter, {
sessionCookie: {
attributes: {
sameSite: "none",
},
},
getUserAttributes: (attributes) => {
return {
github_id: attributes.github_id,
username: attributes.username,
};
},
});
declare module "npm:lucia" {
interface Register {
Lucia: typeof lucia;
DatabaseUserAttributes: DatabaseUserAttributes;
}
}
interface DatabaseUserAttributes {
github_id: number;
username: string;
}
interface GitHubUserResult {
id: number;
login: string; // username
}
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"
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!
September 3, 2024