DEV Community

Alex Spinov
Alex Spinov

Posted on

Lucia Auth Has Been Replaced by a Free Guide That Teaches You Auth From Scratch

Lucia v3 was the most popular auth library for TypeScript. Then the creator did something radical: deprecated the library and wrote a guide instead.

The result? The Copenhagen Book — a free, framework-agnostic guide to implementing authentication from scratch. No magic. No black boxes.

Why Deprecate a Popular Library?

The Lucia team realized: auth libraries create a false sense of security. Developers use them without understanding what happens under the hood. When something breaks, they are helpless.

The Copenhagen Book teaches you to build auth yourself — so you actually understand it.

Session-Based Auth (The Right Way)

// 1. Generate session token
import { sha256 } from "@oslojs/crypto/sha2";
import { encodeHexLowerCase } from "@oslojs/encoding";

function generateSessionToken(): string {
  const bytes = new Uint8Array(20);
  crypto.getRandomValues(bytes);
  return encodeHexLowerCase(bytes);
}

// 2. Create session in database
async function createSession(token: string, userId: string) {
  const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token)));
  await db.insert(sessions).values({
    id: sessionId,
    userId,
    expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), // 30 days
  });
  return sessionId;
}

// 3. Validate session
async function validateSession(token: string) {
  const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token)));
  const session = await db.query.sessions.findFirst({
    where: eq(sessions.id, sessionId),
    with: { user: true },
  });

  if (!session || session.expiresAt < new Date()) {
    if (session) await db.delete(sessions).where(eq(sessions.id, sessionId));
    return { session: null, user: null };
  }

  // Extend session if close to expiry
  if (session.expiresAt.getTime() - Date.now() < 15 * 24 * 60 * 60 * 1000) {
    session.expiresAt = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000);
    await db.update(sessions).set({ expiresAt: session.expiresAt });
  }

  return { session, user: session.user };
}
Enter fullscreen mode Exit fullscreen mode

Password Hashing

import { hash, verify } from "@node-rs/argon2";

// Hash password on registration
const passwordHash = await hash(password, {
  memoryCost: 19456,
  timeCost: 2,
  outputLen: 32,
  parallelism: 1,
});

// Verify on login
const valid = await verify(passwordHash, password);
Enter fullscreen mode Exit fullscreen mode

Always use Argon2id — not bcrypt, not scrypt, not SHA-256.

OAuth2 (Google, GitHub, etc.)

import { Google } from "arctic";

const google = new Google(
  process.env.GOOGLE_CLIENT_ID,
  process.env.GOOGLE_CLIENT_SECRET,
  "http://localhost:3000/auth/google/callback"
);

// 1. Generate authorization URL
const state = generateState();
const codeVerifier = generateCodeVerifier();
const url = google.createAuthorizationURL(state, codeVerifier, ["openid", "email"]);

// 2. Handle callback
const tokens = await google.validateAuthorizationCode(code, codeVerifier);
const response = await fetch("https://openidconnect.googleapis.com/v1/userinfo", {
  headers: { Authorization: `Bearer ${tokens.accessToken()}` },
});
const googleUser = await response.json();
Enter fullscreen mode Exit fullscreen mode

Libraries to Use

  • @oslojs/crypto — cryptographic utilities
  • @oslojs/encoding — encoding helpers
  • arctic — OAuth2 providers (Google, GitHub, Discord, etc.)
  • @node-rs/argon2 — password hashing

All by the Lucia team. Small, focused, no magic.

The Session Cookie

function setSessionCookie(token: string) {
  return {
    name: "session",
    value: token,
    attributes: {
      httpOnly: true,
      secure: true,
      sameSite: "lax" as const,
      path: "/",
      maxAge: 30 * 24 * 60 * 60, // 30 days
    },
  };
}
Enter fullscreen mode Exit fullscreen mode

httpOnly + secure + sameSite = lax — the holy trinity of session cookie security.


Need secure authentication for your app? I build web tools and developer solutions. Email spinov001@gmail.com or check my Apify tools.

Top comments (0)