npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@peopleplus/auth

v0.3.9

Published

This package builds on top of lucia auth to provide handlers with preconfigured behaviour that is in line with the requirements of most if not all apps we maintain and build at PeoplePlus.

Downloads

889

Readme

PeoplePlus Auth

This package builds on top of lucia auth to provide handlers with preconfigured behaviour that is in line with the requirements of most if not all apps we maintain and build at PeoplePlus.

Adding to a project

Tables

Depending on what database backend you are using you may need to add the following to your schema:

classDiagram
    User <|-- Session: user_id
    class User {
      id: string
      auth0_id: string
      ...user attributes
    }
    class Session {
      id: string
      user_id: string
      expires_at: datetime
      id_token: string
      access_token: string
    }

Here's an example drizzle schema:

import { pgTable, text, varchar, timestamp } from 'drizzle-orm/pg-core';

export const usersTable = pgTable('user', {
	id: varchar('id', { length: 15 }).primaryKey(),
	auth0ID: text('auth0_id').notNull(),
});

export const sessionsTable = pgTable('session', {
	id: varchar('id', { length: 128 }).primaryKey(),
	userId: varchar('user_id', { length: 15 })
		.notNull()
		.references(() => usersTable.id),
	expiresAt: timestamp('expires_at', { mode: 'date', withTimezone: true }).notNull(),
	accessToken: text('access_token').notNull(),
	idToken: text('id_token').notNull(),
});

Set Up

Install this package

npm i -D @peopleplus/auth

Create a file at $lib/server/auth.ts with content similar to this:

import { dev } from '$app/environment';
import { queryClient } from './database';
import { PeoplePlusAuth } from '@peopleplus/auth';
import { PostgresAdapter } from '@peopleplus/auth/database/postgres';
import {
	PRIVATE_AUTH0_API_IDENTIFIER,
	PRIVATE_AUTH0_CLIENT_ID,
	PRIVATE_AUTH0_CLIENT_SECRET,
	PRIVATE_AUTH0_DOMAIN,
} from '$env/static/private';

declare module '@peopleplus/auth' {
	interface Provide {
		Auth: Auth;
		DatabaseUserAttributes: DatabaseUserAttributes;
	}
}

export type Auth = typeof auth;
type DatabaseUserAttributes = {
	// Custom user attributes here
};

export const auth = new PeoplePlusAuth({
	dev,
	adapter: new PostgresAdapter(queryClient, {
		user: 'user',
		session: 'session',
	}),
	auth0: {
		domain: PRIVATE_AUTH0_DOMAIN,
		clientID: PRIVATE_AUTH0_CLIENT_ID,
		clientSecret: PRIVATE_AUTH0_CLIENT_SECRET,
		audience: PRIVATE_AUTH0_API_IDENTIFIER,
	},

	// The following can all be omitted if you don't need/want to pick out any extra atributes
	createUserAttributes({ idToken }) {
		return {
			// Pick out any details from the idToken that you want to save on the user
		};
	},
	exposeUserAttributes(attributes) {
		return {
			// Expose any attributes of the user from the database, for example:
			auth0ID: attributes.auth0_id,
		};
	},
	exposeSessionAttributes(attributes) {
		return {
			// Expose any attributes of the session from the database
		};
	},
});

export const { handleAuthCallback, handleSignInRedirect, handleSignOut, hook } = auth.handlers();

Set up your route handlers and actions:

// e.g. routes/auth/callback/+server.ts
export { handleAuthCallback as GET } from '$lib/server/auth';
// e.g. routes/auth/signin/+server.ts
export { handleSignInRedirect as GET } from '$lib/server/auth';
// e.g. routes/+page.server.ts
import { handleSignOut } from '$lib/server/auth';
export const actions = { logout: handleSignOut };
/// hooks.server.ts
import { hook } from '$lib/server/auth';
export const handle = hook;
/// app.d.ts

declare global {
	namespace App {
		interface Locals {
			user: import('@peopleplus/auth').User | null;
			session: import('@peopleplus/auth').Session | null;
		}
	}
}

export {};

Access the session from your server load functions

export async function load({ locals }) {
	// Session and user is null if the user isn't logged in
	const { user, session } = locals;

	// Note: depending on what you choose to expose on your session and user,
	// you may want to pick and choose which data to pass down the the client
	// to avoid leaking information.
	return { session, user };
}

Add sign in / sign out buttons

To sign in, simply direct the user to your chosen sign in route:

<a href="/auth/signin">Sign in</a>

To sign out, create a form that posts to your logout action

<form method="post" action="/?/logout">
	<button>Sign out</button>
</form>

Considerations

Why not Auth.js?

Auth.js was originally chosen as the authentication solution for our SvelteKit apps. Over time we realised many shortcomings of Auth.js and found ourselves battling with the lack of flexibilty it provided.

A few of the issues we ran in to:

  • Lack of federated logout (logging out of auth0 after logging out of the app), with friction around adding it manually
  • Need to manually update fields in the database when a user logs in (e.g. access token, profile data etc.)
  • No cosmos database adapter, we had to create our own.
  • Sign in/out cannot be done (by default) in a server side friendly way, as the APIs provided assume a browser environment. You're on your own to implement this server side.
  • Auth.js assumes/expects that only one user exists per email. It either straight up denies it, or you can allow account linking by email but this no the desired behaviour. We need to, for example, support the case where a user has two distinct auth0 accounts with the same email and allow this to map to two distict users on the app side. (This was the final nail in the coffin.)

Why Lucia?

Lucia takes a slightly different approach to Auth.js' all inclusive apprach. Lucia provides JS APIs to create your auth implementation. This is more work than Auth.js, but it allows authentication to be tailored to our specific needs very naturally (read: without a mountain of hacks).

The downside is this would mean we would need to reimplement the handlers in every project, and this is where this package comes in. In a way it could be thought of as our very own custom Auth.js. It effectively adds back the opinion to Lucia, but this time it is our opinion!