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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@splendidlabz/auth

v1.1.2

Published

A comprehensive authentication system built specifically for Astro applications. This package provides both server-side and client-side authentication components with support for database sessions or stateless cookie-based authentication.

Readme

Auth Package

A comprehensive authentication system built specifically for Astro applications. This package provides both server-side and client-side authentication components with support for database sessions or stateless cookie-based authentication.

Installation

npm install @splendidlabz/auth

For Drizzle ORM support, also install:

npm install drizzle-orm
# Choose your database driver:
npm install better-sqlite3  # For SQLite
npm install pg              # For PostgreSQL
npm install mysql2          # For MySQL

Basic Usage

This auth package supports two authentication strategies and two database options:

Authentication Strategies

1. Cookie Strategy (Stateless)

Uses signed cookies to store session data. No database sessions required.

// src/services/auth.js
import { auth as splAuth } from '@splendidlabz/auth'
import { db, User } from 'astro:db'

export const auth = splAuth({
  db, // Still needed for user lookup
  tables: { User }, // Only User table required
  auth: {
    strategy: 'cookie',
    secret: process.env.AUTH_SECRET, // Required for cookie signing
    userAttributes: ['email', 'first_name', 'name'],
    cookieOptions: {
      maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
      httpOnly: true,
      secure: process.env.NODE_ENV === 'production',
      sameSite: 'lax',
    },
  },
})

2. Database Strategy (Stateful)

Stores sessions in the database for centralized session management.

// src/services/auth.js
import { auth as splAuth } from '@splendidlabz/auth'
import { db, User, Session, PasswordResetToken } from 'astro:db'

export const auth = splAuth({
  db,
  tables: { User, Session, PasswordResetToken },
  auth: {
    strategy: 'db',
    userAttributes: ['email', 'first_name', 'name'],
  },
})

Database Options

Option 1: Using Astro DB (Recommended)

// src/services/auth.js
import { auth as splAuth } from '@splendidlabz/auth'
import { db, User, Session, PasswordResetToken } from 'astro:db'

export const auth = splAuth({
  db,
  tables: { User, Session, PasswordResetToken },
  auth: {
    strategy: 'db', // or 'cookie'
    userAttributes: ['email', 'first_name', 'name'],
  },
})

Option 2: Using Drizzle ORM

// src/services/auth.js
import { auth as splAuth } from '@splendidlabz/auth'
import { drizzle } from 'drizzle-orm/better-sqlite3'
import Database from 'better-sqlite3'
import { users, sessions, passwordResetTokens } from './db/schema.js'

const sqlite = new Database('auth.db')
const db = drizzle(sqlite)

export const auth = splAuth({
  db,
  tables: {
    User: users,
    Session: sessions, // Only for 'db' strategy
    PasswordResetToken: passwordResetTokens,
  },
  auth: {
    strategy: 'db', // or 'cookie'
    userAttributes: ['email', 'firstName', 'name'],
  },
})

Web/Client-side Setup

// src/services/web/auth.js
import { webAuth as wa } from '@splendidlabz/auth/web'

export const webAuth = wa({
  tokenName: 'auth_token',
  authEndpoint: '/api/auth/authenticate/',
})

Basic Implementation

1. Login Page

---
// src/pages/auth/login.astro
import { auth } from '@/services/auth.js'
import { parseData } from '@splendidlabz/astro/server'

const redirectTo = Astro.url.searchParams.get('redirect_to')
export const prerender = false

let error

if (Astro.request.method === 'POST') {
  const body = await parseData(Astro)
  const { email, password } = body

  try {
    await auth.login(Astro, { email, password })
    return Astro.redirect(redirectTo || '/dashboard/')
  } catch (e) {
    error = e
  }
}
---

<h1>Login</h1>
<form method="POST">
  <input type="email" name="email" placeholder="Email" required />
  <input type="password" name="password" placeholder="Password" required />
  <button type="submit">Log in</button>
</form>
{error && <p class="error">{error.message}</p>}

2. Middleware Protection

// src/middleware/index.js
import { auth } from '@/services/auth.js'
import { AuthManager } from '@splendidlabz/utils'
import { sequence } from 'astro:middleware'

export const onRequest = sequence(authenticate)

const { isProtectedRoute } = AuthManager({
  protectedRoutes: {
    is: ['/dashboard/'],
    startsWith: ['/account'],
  },
})

async function authenticate(context, next) {
  if (!isProtectedRoute(context.url.pathname)) return next()

  try {
    await auth.authenticate(context)
    return next()
  } catch (e) {
    const originalPathname = context.url.pathname
    return context.redirect(`/auth/login/?redirect_to=${originalPathname}`)
  }
}

3. API Routes

// src/pages/api/auth/authenticate.js
import { auth } from '@/services/auth.js'

export async function POST(context) {
  try {
    const result = await auth.authenticate(context)
    return new Response(JSON.stringify(result), {
      status: 200,
      headers: { 'Content-Type': 'application/json' },
    })
  } catch (error) {
    return new Response(JSON.stringify({ error: error.message }), {
      status: 401,
      headers: { 'Content-Type': 'application/json' },
    })
  }
}

Config

Strategy Comparison

| Feature | Database Strategy | Cookie Strategy | | ------------------------ | ---------------------------------------- | --------------------------- | | Session Storage | Database tables | Signed cookies | | State | Stateful | Stateless | | Database Requirement | User, Session, PasswordResetToken tables | Only User table | | Server Memory | Low | Very low | | Security | Session invalidation possible | Relies on cookie expiration | | Logout | Immediate (removes from DB) | Client-side only | | Session Management | Centralized | Distributed | | Best For | Multi-device apps, admin panels | Simple apps, APIs |

Use Database Strategy when:

  • You need immediate session invalidation
  • Building multi-device applications
  • Want centralized session management
  • Need to track active sessions

Use Cookie Strategy when:

  • Building stateless applications
  • Want minimal database overhead
  • Working with microservices/APIs
  • Don't need session tracking

Configuration Options

Server Auth Configuration

export const auth = splAuth({
  db, // Database instance (required)
  tables: {}, // Database tables (required)
  auth: {
    strategy, // 'db' or 'cookie' (required)
    secret, // Required for 'cookie' strategy
    userAttributes, // Array of user fields to include in session
    cookieOptions, // Cookie settings (cookie strategy only)
    additionalVerification, // Custom verification function
  },
})

Parameters:

  • db: Your database instance (Astro DB or Drizzle)
  • tables: Object mapping table names to table schemas
  • auth.strategy: 'db' for database sessions, 'cookie' for stateless
  • auth.secret: Secret key for cookie signing (required for cookie strategy)
  • auth.userAttributes: Array of user fields to include in session data
  • auth.cookieOptions: Cookie configuration object (cookie strategy only)
  • auth.additionalVerification: Optional async function for custom verification

Cookie Options (Cookie Strategy Only)

cookieOptions: {
  maxAge: 7 * 24 * 60 * 60 * 1000, // Cookie lifetime in milliseconds
  httpOnly: true,                   // Prevent client-side access
  secure: true,                     // HTTPS only in production
  sameSite: 'lax',                 // CSRF protection
}

Web Auth Configuration

export const webAuth = wa({
  tokenName: 'auth_token', // Token name in storage
  authEndpoint: '/api/auth/authenticate/', // Authentication API endpoint
})

Database Schema

Astro DB Schema

// db/config.ts
import { defineDb, defineTable, column } from 'astro:db'

const User = defineTable({
  columns: {
    id: column.text({ primaryKey: true }),
    email: column.text({ unique: true }),
    password: column.text(),
    first_name: column.text({ optional: true }),
    name: column.text({ optional: true }),
    created_at: column.date({ default: new Date() }),
  },
})

// Only needed for 'db' strategy
const Session = defineTable({
  columns: {
    id: column.text({ primaryKey: true }),
    user_id: column.text({ references: () => User.columns.id }),
    expires_at: column.date(),
    created_at: column.date({ default: new Date() }),
  },
})

const PasswordResetToken = defineTable({
  columns: {
    id: column.text({ primaryKey: true }),
    user_id: column.text({ references: () => User.columns.id }),
    token: column.text({ unique: true }),
    expires_at: column.date(),
    created_at: column.date({ default: new Date() }),
  },
})

export default defineDb({
  tables: { User, Session, PasswordResetToken },
})

Drizzle Schema (SQLite)

// src/db/schema.js
import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'
import { sql } from 'drizzle-orm'

export const users = sqliteTable('users', {
  id: text('id').primaryKey(),
  email: text('email').unique().notNull(),
  password: text('password').notNull(),
  firstName: text('first_name'),
  name: text('name'),
  createdAt: integer('created_at', { mode: 'timestamp' }).default(
    sql`CURRENT_TIMESTAMP`,
  ),
})

// Only needed for 'db' strategy
export const sessions = sqliteTable('sessions', {
  id: text('id').primaryKey(),
  userId: text('user_id')
    .notNull()
    .references(() => users.id),
  expiresAt: integer('expires_at', { mode: 'timestamp' }).notNull(),
  createdAt: integer('created_at', { mode: 'timestamp' }).default(
    sql`CURRENT_TIMESTAMP`,
  ),
})

export const passwordResetTokens = sqliteTable('password_reset_tokens', {
  id: text('id').primaryKey(),
  userId: text('user_id')
    .notNull()
    .references(() => users.id),
  token: text('token').unique().notNull(),
  expiresAt: integer('expires_at', { mode: 'timestamp' }).notNull(),
  createdAt: integer('created_at', { mode: 'timestamp' }).default(
    sql`CURRENT_TIMESTAMP`,
  ),
})

Drizzle Schema (PostgreSQL)

// src/db/schema.js
import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'
import { sql } from 'drizzle-orm'

export const users = pgTable('users', {
  id: uuid('id').defaultRandom().primaryKey(),
  email: text('email').unique().notNull(),
  password: text('password').notNull(),
  firstName: text('first_name'),
  name: text('name'),
  createdAt: timestamp('created_at').default(sql`CURRENT_TIMESTAMP`),
})

// Only needed for 'db' strategy
export const sessions = pgTable('sessions', {
  id: uuid('id').defaultRandom().primaryKey(),
  userId: uuid('user_id')
    .notNull()
    .references(() => users.id),
  expiresAt: timestamp('expires_at').notNull(),
  createdAt: timestamp('created_at').default(sql`CURRENT_TIMESTAMP`),
})

export const passwordResetTokens = pgTable('password_reset_tokens', {
  id: uuid('id').defaultRandom().primaryKey(),
  userId: uuid('user_id')
    .notNull()
    .references(() => users.id),
  token: text('token').unique().notNull(),
  expiresAt: timestamp('expires_at').notNull(),
  createdAt: timestamp('created_at').default(sql`CURRENT_TIMESTAMP`),
})

Complete Drizzle Setup

1. Install Dependencies

npm install drizzle-orm better-sqlite3
npm install -D drizzle-kit @types/better-sqlite3

2. Database Configuration

// src/db/index.js
import { drizzle } from 'drizzle-orm/better-sqlite3'
import Database from 'better-sqlite3'
import * as schema from './schema.js'

const sqlite = new Database('auth.db')
export const db = drizzle(sqlite, { schema })

3. Drizzle Configuration

// drizzle.config.js
export default {
  schema: './src/db/schema.js',
  out: './drizzle',
  driver: 'better-sqlite',
  dbCredentials: {
    url: './auth.db',
  },
}

4. Generate Migrations

# Generate migration
npx drizzle-kit generate:sqlite

# Run migration
npx drizzle-kit push:sqlite

Environment Variables

# .env
DATABASE_URL=your_database_url
AUTH_SECRET=your_secret_key_for_cookie_signing

# For cookie strategy, AUTH_SECRET is required
# Generate a strong secret: openssl rand -base64 32

Server Methods

auth.login(context, credentials)

await auth.login(Astro, { email: '[email protected]', password: 'password' })

auth.authenticate(context)

const user = await auth.authenticate(Astro)

auth.logout(context)

await auth.logout(Astro)

auth.createUser(userData)

const user = await auth.createUser({
  email: '[email protected]',
  password: 'password',
  first_name: 'John',
  name: 'John Doe',
})

Web/Client Methods

webAuth.isAuthenticated()

const isAuth = await webAuth.isAuthenticated()

webAuth.getUser()

const user = await webAuth.getUser()

webAuth.logout()

await webAuth.logout()

Security Notes

CSRF Protection

CSRF protection is enabled by default in Astro v5.0:

// astro.config.mjs
export default defineConfig({
  output: 'server',
  security: { checkOrigin: true }, // Default in Astro 5.0
})

Advanced Verification

Add custom verification logic:

export const auth = splAuth({
  // ... other config
  auth: {
    strategy: 'db',
    userAttributes: ['email', 'first_name', 'name'],
    async additionalVerification(context, { user }) {
      // Check subscriptions, permissions, etc.
      const subscriptions = await getUserSubscriptions(user.id)
      const permissions = await getUserPermissions(user.id)

      return {
        subscriptions,
        permissions,
        product_access: subscriptions.length > 0,
      }
    },
  },
})

Troubleshooting

Database Deployment with Astro DB

When using astro:db, you cannot use astro db execute <file-path> --remote directly. Instead:

  1. Run npx astro dev --remote
  2. Create an Astro page with your database operations
  3. Navigate to that page once to execute the operations