DEV Community

Alex Spinov
Alex Spinov

Posted on

Lucia Has a Free Auth Library — Simple Session-Based Authentication for TypeScript

Most auth libraries try to do everything. Lucia does one thing well — session management. No magic, no hidden complexity, just sessions that work.

What is Lucia?

Lucia is a lightweight authentication library for TypeScript. It handles session creation, validation, and invalidation. You bring your own database and login methods. Lucia manages the session lifecycle.

Why Lucia's Approach Works

1. Minimal, Understandable API

import { Lucia } from "lucia";
import { DrizzleSQLiteAdapter } from "@lucia-auth/adapter-drizzle";

const adapter = new DrizzleSQLiteAdapter(db, sessionTable, userTable);

export const lucia = new Lucia(adapter, {
  sessionCookie: {
    attributes: {
      secure: process.env.NODE_ENV === "production",
    },
  },
  getUserAttributes: (attributes) => {
    return { username: attributes.username };
  },
});
Enter fullscreen mode Exit fullscreen mode

2. Framework Agnostic

// Next.js
import { cookies } from "next/headers";

export async function getUser() {
  const sessionId = cookies().get(lucia.sessionCookieName)?.value ?? null;
  if (!sessionId) return null;

  const { session, user } = await lucia.validateSession(sessionId);
  return user;
}
Enter fullscreen mode Exit fullscreen mode
// Express
app.use(async (req, res, next) => {
  const sessionId = lucia.readSessionCookie(req.headers.cookie ?? "");
  if (!sessionId) { req.user = null; return next(); }

  const { session, user } = await lucia.validateSession(sessionId);
  req.user = user;
  next();
});
Enter fullscreen mode Exit fullscreen mode
// SvelteKit
export const handle: Handle = async ({ event, resolve }) => {
  const sessionId = event.cookies.get(lucia.sessionCookieName);
  if (!sessionId) { event.locals.user = null; return resolve(event); }

  const { session, user } = await lucia.validateSession(sessionId);
  event.locals.user = user;
  return resolve(event);
};
Enter fullscreen mode Exit fullscreen mode

3. Login Implementation (You Control It)

// Sign up
app.post("/signup", async (req, res) => {
  const { username, password } = req.body;

  const hashedPassword = await new Argon2id().hash(password);

  const userId = generateId(15);
  await db.insert(userTable).values({
    id: userId,
    username,
    hashedPassword,
  });

  const session = await lucia.createSession(userId, {});
  const cookie = lucia.createSessionCookie(session.id);
  res.setHeader("Set-Cookie", cookie.serialize());
  res.redirect("/dashboard");
});
Enter fullscreen mode Exit fullscreen mode
// Sign in
app.post("/signin", async (req, res) => {
  const { username, password } = req.body;

  const user = await db.query.userTable.findFirst({
    where: eq(userTable.username, username),
  });

  if (!user) return res.status(401).send("Invalid credentials");

  const valid = await new Argon2id().verify(user.hashedPassword, password);
  if (!valid) return res.status(401).send("Invalid credentials");

  const session = await lucia.createSession(user.id, {});
  const cookie = lucia.createSessionCookie(session.id);
  res.setHeader("Set-Cookie", cookie.serialize());
  res.redirect("/dashboard");
});
Enter fullscreen mode Exit fullscreen mode

4. OAuth Support

import { GitHub } from "arctic";

const github = new GitHub(clientId, clientSecret);

// Start OAuth flow
app.get("/login/github", async (req, res) => {
  const state = generateState();
  const url = github.createAuthorizationURL(state, ["user:email"]);
  res.redirect(url.toString());
});

// Handle callback
app.get("/login/github/callback", async (req, res) => {
  const tokens = await github.validateAuthorizationCode(req.query.code);
  const githubUser = await fetch("https://api.github.com/user", {
    headers: { Authorization: `Bearer ${tokens.accessToken}` },
  }).then(r => r.json());

  // Create or find user, create session...
  const session = await lucia.createSession(userId, {});
  // ...
});
Enter fullscreen mode Exit fullscreen mode

5. Database Adapters

Supported:
- Drizzle ORM (SQLite, PostgreSQL, MySQL)
- Prisma
- Mongoose
- Better-sqlite3
- unstorage (Redis, Cloudflare KV, etc.)
Enter fullscreen mode Exit fullscreen mode

Lucia vs NextAuth vs Clerk

Lucia NextAuth/Auth.js Clerk
Philosophy Sessions only, you build login Full auth solution Managed service
Complexity Low Medium Low (hosted)
Control Full Partial Limited
Database Your choice Adapters Managed
OAuth Via Arctic Built-in providers Built-in
Lock-in Zero Low High
Price Free Free Freemium

Getting Started

npm install lucia
npm install @lucia-auth/adapter-drizzle  # or your adapter
npm install arctic  # for OAuth
Enter fullscreen mode Exit fullscreen mode

The Bottom Line

Lucia gives you exactly what you need for auth and nothing more. If you want to understand every line of your authentication code, Lucia is the right choice.


Need data tools? I build scraping solutions. Check my Apify actors or email spinov001@gmail.com.

Top comments (0)