pwshub.com

Lucia Auth: An Auth.js alternative for Next.js authentication

Authentication is a critical component of any web application, and choosing the right authentication library can significantly impact your development experience. In the Next.js ecosystem, two popular options have emerged: Auth.js (formerly NextAuth.js) and Lucia Auth.

Lucia Auth: An Auth.js Alternative For Next.js Authentication

This article will provide a practical, code-based comparison of the basics of these libraries to make it easy for you to pick which one is best suited for your Next.js project. This article assumes you have:

  • Working knowledge of the basics of authentication and sessions
  • Working knowledge of Next.js and the App Router

Comparing Auth.js and Lucia Auth

Auth.js, formerly known as NextAuth.js, is a feature-rich solution that has been a staple in the Next.js community for years. It offers a wide range of built-in providers and a plugin system for extensibility. It was created to simplify the implementation of authentication in web applications, with a focus on ease of use and quick setup.

Auth.js follows a batteries-included paradigm that aims to make authentication as simple as possible for developers. The paradigm abstracts away much of the complexity of authentication, allowing developers to set up secure authentication flows with minimal code.

In contrast, Lucia is an authentication library designed to be simple and flexible, with a focus on type safety and adaptability to various frameworks and databases. Lucia follows a minimalist and flexible paradigm, providing building blocks to handle the most tedious aspects of authentication while leaving the implementation details to you, allowing you a much greater degree of flexibility and control.

The primary feature difference between these two libraries is that Auth.js supports both sessions stored in a database and sessions based on JWTs (JSON Web Tokens). Meanwhile, all Lucia sessions must be stored in a database. Additionally, when authenticating with a username and password, i.e., credentials, Auth.js doesn’t support using database sessions. Here is a quote from their official docs:

The Credentials provider only supports the JWT session strategy. You can still create and save a database session and reference it from the JWT via an id, but you’ll need to provide that logic yourself.

Lucia Auth vs. Auth.js in practice

In this section, we’ll get a better feel for the differences between Auth.js and Lucia by comparing code examples of them in action. The core of both Lucia and Auth.js is session management, so we’ll look at what it takes to initialize each library and their approaches to creating, accessing, and invalidating user sessions.

Demo authentication with Lucia

Lucia requires a database, so when you install it, you’ll also need to install a database driver and a matching Lucia adapter. Once you have a driver and adapter installed, you can initialize Lucia with the Lucia constructor, which takes your database adapter as the first argument and a configuration object as its second.

Here’s how you might initialize Lucia with the better-sqlite driver and matching adapter:

import { Lucia } from "lucia";a
import { BetterSqlite3Adapter } from "@lucia-auth/adapter-sqlite";
import sqlite from "better-sqlite3";
//initialize the db driver
export const db = sqlite("./demo.db");
//initialize the adapter
const adapter = new BetterSqlite3Adapter(db, {
// lets you decide which tables in the db you want to access and what names to give them
  user: "user",
  session: "session",
});
export const lucia = new Lucia(adapter, {
  sessionCookie: {
    // this sets cookies with super long expiration
    // since Next.js doesn't allow Lucia to extend cookie expiration when rendering pages
    expires: false,
    attributes: {
      // set to `true` when using HTTPS
      secure: process.env.NODE_ENV === "production",
    },
  },
  // this function lets you specify which columns of your user table
  // you'll be able to access inside your app
  // by default, Lucia doesn't expose any columns
  getUserAttributes: (attributes) => {
    return {
      username: attributes.username,
    };
  },
});

Now that Lucia has been initialized, we can use the lucia object to manage user sessions. With Lucia, session creation is explicit: after you’ve verified/registered a user’s credentials, you create a session like this:

const session = await lucia.createSession(userId, {});
const sessionCookie = lucia.createSessionCookie(session.id);
cookies().set(
  sessionCookie.name,
  sessionCookie.value,
  sessionCookie.attributes
);

The first line creates a record in the sessions table of your database that contains a generated session ID, the ID of the authorized user, and the session’s expiration date. The second line generates the data for a cookie representing the session. And the last statement uses Next.js’ cookie utility to create the cookie.

Now that you’ve created a session and stored a reference to it inside a cookie, you can access it later using code like this:

const session = cookies().get(lucia.sessionCookieName);
  if (!session) {
    return
  }
  const sessionId = session.value;
  if (!sessionId) {
    return
  }
  const result = await lucia.validateSession(sessionId);
  return result; 

First, you get the session ID from the cookie you created before, then you call the lucia.validateSession method with the session ID as an argument. The lucia.validateSession method returns an object with two properties: session and user.

If validateSession decides that the session ID passed to it is valid, the value of the session property will be an object that contains session information, and the user property will contain the data you exposed in the getUserAttributes method when you initialized Lucia. If the session ID you pass is invalid, both the session and user properties will be null.

Lucia extends the expiration date of a session whenever it’s used, so you don’t need to worry about that. Finally, you can invalidate a session with the following code:

await lucia.invalidateSession(sessionId);

When you invalidate a session, you should delete the session cookie that references it by overwriting it with a blank cookie like so:

const sessionCookie = lucia.createBlankSessionCookie();
cookies().set(
  sessionCookie.name,
  sessionCookie.value,
  sessionCookie.attributes
);

Now, let’s look at Auth.js.

Demo authentication with Auth.js

Auth.js doesn’t support database sessions for credentials-based authentication, so we won’t worry about them here. The setup for Auth.js comes down to creating an auth.js config file and a /app/api/auth/[...nextauth]/route.js route handler. The route handler should contain this code:

export const { GET, POST } = handlers

And auth.js should contain this code:

import NextAuth from "next-auth"
import Credentials from "next-auth/providers/credentials"
export const { handlers, signIn, signOut, auth } = NextAuth({
  providers: [
    Credentials({
      // You can specify which fields should be submitted, by adding keys to the `credentials` object.
      // e.g. domain, username, password, 2FA token, etc.
      credentials: {
        email: {},
        password: {},
      },
      authorize: async (credentials) => {
        let user = null
        // logic to verify if the user exists
        user = await getUserFromDb(credentials.email, password)
        if (!user) {
          throw new Error("User not found.")
        }
        // return user object
        return user
      },
    }),
  ],
})

And that’s it for Auth.js. The details of creating the sessions, managing them, and storing references to them are almost entirely handled for you by the functions the NextAuth method returns. To sign in an authorized user, this is all the code you need:

import { signIn } from "@/auth"
async function signUserIn (formData) {
  "use server"
  await signIn("credentials", formData)
}}

The signOut method handles session invalidation and the auth method provides you access to the data stored inside the session.

Conclusion

In this article, we explored the key differences between Auth.js and Lucia Auth, focusing on their features and design paradigms. Auth.js provides a streamlined solution that lets you get up and running quickly, while Lucia offers a minimal toolkit that emphasizes greater customization.

At the time of writing, Lucia has a nearly-empty Issues page on GitHub, indicating a bug-free experience. On the other hand, Auth.js has hundreds, many of them appearing to be edge-case bugs related to less popular providers. Some bugs lack timely support from the maintainers, leading Next.js users to instead rely on community-submitted workarounds and unmerged PRs.

If you’ve used Lucia or Auth.js before, which do you prefer and why? Let us know in the comments below!

Would you be interested in joining LogRocket's developer community?

Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.

Sign up now

Source: blog.logrocket.com

Related stories
6 days ago - Auth.js makes adding authentication to web apps easier and more secure. Let's discuss why you should use it in your projects. The post Auth.js adoption guide: Overview, examples, and alternatives appeared first on LogRocket Blog.
3 weeks ago - Learn how to use the provide/ inject function pair with the Composition API to pass data into deeply nested components in Vue.js 3. The post Using provide/inject in Vue.js 3 with the Composition API appeared first on LogRocket Blog.
1 month ago - Bookkeeping is a process in which a company systematically collects, records, organizes, and tracks its financial data daily throughout the year. Bookkeeping is important for businesses to ensure that every transaction, income, expense,...
2 weeks ago - The most magical thing about the golden ratio is how artists and architects have considered the problem of proportion in history. The post Using the golden ratio in UX design appeared first on LogRocket Blog.
2 weeks ago - Let’s get ready for September with a fresh collection of desktop wallpapers! Created with love by the community for the community, they come in versions with and without a calendar. Enjoy!
Other stories
1 hour ago - This release candidate, a near-final look at Deno 2, includes the addition of Node's process global, better dependency management, and various API stabilizations, and more.
1 hour ago - Published: September 19, 2024 The CSS Working Group has combined the two CSS masonry proposals into one draft specification. The group hopes that...
1 hour ago - Stay organized with collections Save and categorize content based on your preferences. Published: September...
3 hours ago - DNS monitoring tool is a cloud-based scanner that constantly monitors DNS records and servers for anomalies. This tool aims to ensure that users are sent to genuine and intended website pages, instead of fakes or replicas. It alerts users...
4 hours ago - Email spoofing is a malicious tactic in which cybercriminals send fake emails that look like they come from trusted organizations or individuals. When unsuspecting users act on these emails, they may unknowingly share sensitive data,...