Created
April 11, 2024 19:57
-
-
Save Blankeos/38167c13bea013ef54065832a7cd32da to your computer and use it in GitHub Desktop.
A better-documneted Lucia + ElysiaJS auth middleware.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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