DEV Community

Alex Spinov
Alex Spinov

Posted on

Lucia Auth Has a Free API — Lightweight Session-Based Authentication

Lucia is a lightweight auth library that handles sessions and cookies. No magic, no opinions — just clean session management you understand and control.

Why Lucia?

  • Simple — ~200 lines of core code, easy to understand
  • Session-based — secure cookies, not JWTs
  • Any database — bring your own adapter
  • Any framework — Next.js, SvelteKit, Astro, Express

Quick Start

npm install lucia oslo @lucia-auth/adapter-drizzle
Enter fullscreen mode Exit fullscreen mode
import { Lucia } from 'lucia';
import { DrizzlePostgreSQLAdapter } from '@lucia-auth/adapter-drizzle';

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

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

Sign Up

import { generateId } from 'lucia';
import { Argon2id } from 'oslo/password';

async function signUp(email: string, password: string, name: string) {
  const hashedPassword = await new Argon2id().hash(password);
  const userId = generateId(15);

  await db.insert(userTable).values({
    id: userId,
    email,
    name,
    hashed_password: hashedPassword,
  });

  const session = await lucia.createSession(userId, {});
  const sessionCookie = lucia.createSessionCookie(session.id);
  return sessionCookie;
}
Enter fullscreen mode Exit fullscreen mode

Sign In

async function signIn(email: string, password: string) {
  const user = await db.query.users.findFirst({
    where: eq(userTable.email, email),
  });

  if (!user) throw new Error('Invalid credentials');

  const validPassword = await new Argon2id().verify(
    user.hashed_password,
    password
  );
  if (!validPassword) throw new Error('Invalid credentials');

  const session = await lucia.createSession(user.id, {});
  return lucia.createSessionCookie(session.id);
}
Enter fullscreen mode Exit fullscreen mode

Validate Session

async function validateRequest(request: Request) {
  const cookieHeader = request.headers.get('Cookie');
  const sessionId = lucia.readSessionCookie(cookieHeader ?? '');

  if (!sessionId) return { user: null, session: null };

  const result = await lucia.validateSession(sessionId);
  return result;
}
Enter fullscreen mode Exit fullscreen mode

Next.js Middleware

import { NextResponse } from 'next/server';

export async function middleware(request: NextRequest) {
  const { user } = await validateRequest(request);

  if (!user && request.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.redirect(new URL('/login', request.url));
  }

  return NextResponse.next();
}
Enter fullscreen mode Exit fullscreen mode

OAuth

import { GitHub } from 'arctic';

const github = new GitHub(clientId, clientSecret);

// Create authorization URL
const [url, state] = await github.createAuthorizationURL();

// Handle callback
const tokens = await github.validateAuthorizationCode(code);
const githubUser = await fetch('https://api.github.com/user', {
  headers: { Authorization: `Bearer ${tokens.accessToken}` },
}).then(r => r.json());

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

Building secure apps? Check out my Apify actors for web scraping, or email spinov001@gmail.com for custom auth solutions.

Lucia, Better Auth, or Auth.js — which auth do you prefer? Share below!

Top comments (0)