Skip to content

Instantly share code, notes, and snippets.

@Blankeos
Created April 11, 2024 19:57
Show Gist options
  • Select an option

  • Save Blankeos/38167c13bea013ef54065832a7cd32da to your computer and use it in GitHub Desktop.

Select an option

Save Blankeos/38167c13bea013ef54065832a7cd32da to your computer and use it in GitHub Desktop.
A better-documneted Lucia + ElysiaJS auth middleware.
import { lucia } from "@/lib/lucia";
import Elysia from "elysia";
import { verifyRequestOrigin } from "lucia";
import type { User, Session } from "lucia";
/**
* Use this middleware where you want to check if a user is authenticated.
*
* As a black box, you'll be able to see if the user is authenticated via
* - `ctx?.user`; or
* - `ctx?.session`
*
* From Official Lucia + Elysia docs: {@link https://lucia-auth.com/guides/validate-session-cookies/elysia}
*
* @example
* // 1️⃣ For scoped use in a few specific routes:
* .guard({}, (app) =>
* app
* .use(authMiddleware)
* .get("/my-route-1", async (ctx) => {
* return `Able to access the derived values here: ${ctx?.user}`;
* })
* .get("/my-route-2", async (ctx) => {
* return `Able to access it here as well! ${ctx.user}.`;
* })
* )
* .get("/", async (ctx) => {
* return `Not able to access ctx.user here.`;
* })
*
* @example
* // 2️⃣ For scoped use in controllers
* export const authController = new Elysia({
* name: "auth",
* prefix: "/auth",
* })
* .use(authMiddleware)
* .get("/my-route-1", async (ctx) => {
* return `Able to access the derived values here: ${ctx?.user}`;
* })
*
* @todo console.log should be replaced by loggers.
*/
export const authMiddleware = new Elysia().derive(
{ as: "scoped" },
async (
context
): Promise<{
user: User | null;
session: Session | null;
}> => {
// CSRF check
console.log("[authMiddleware] CSRF check started");
if (context.request.method !== "GET") {
const originHeader = context.request.headers.get("Origin");
// NOTE: You may need to use `X-Forwarded-Host` instead
const hostHeader = context.request.headers.get("Host");
if (
!originHeader ||
!hostHeader ||
!verifyRequestOrigin(originHeader, [hostHeader])
) {
console.log("[authMiddleware] CSRF check failed");
return {
user: null,
session: null,
};
}
}
console.log("[authMiddleware] CSRF check passed");
// use headers instead of Cookie API to prevent type coercion
const cookieHeader = context.request.headers.get("Cookie") ?? "";
console.log("[authMiddleware] Reading session cookie");
const sessionId = lucia.readSessionCookie(cookieHeader);
if (!sessionId) {
console.log("[authMiddleware] No session cookie found");
return {
user: null,
session: null,
};
}
console.log("[authMiddleware] Validating session");
const { session, user } = await lucia.validateSession(sessionId);
if (session && session.fresh) {
console.log("[authMiddleware] Session is fresh, updating cookie");
const sessionCookie = lucia.createSessionCookie(session.id);
context.cookie[sessionCookie.name].set({
value: sessionCookie.value,
...sessionCookie.attributes,
});
}
if (!session) {
console.log("[authMiddleware] Session is invalid, creating blank cookie");
const sessionCookie = lucia.createBlankSessionCookie();
context.cookie[sessionCookie.name].set({
value: sessionCookie.value,
...sessionCookie.attributes,
});
}
console.log("[authMiddleware] Done");
return {
user,
session,
};
}
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment