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
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,
}),
});
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;
}
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);
}
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;
}
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();
}
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, {});
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)