
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
@drop-in/pass
Advanced tools
Your drop-in season pass. aka Auth
A secure, modern authentication library for SvelteKit applications with HttpOnly JWT cookies, refresh token rotation, and comprehensive session management. Runtime agnostic - works in Node.js, Cloudflare Workers, Deno, Bun, and other environments.
npm install @drop-in/pass
Database is provided via dependency injection only.
# .env
DATABASE_URL="postgresql://user:password@localhost:5432/mydb"
JWT_SECRET="your-secret-key-here"
Database is provided via dependency injection only; pass your Drizzle instance to create_session_handle(db) and create_pass_routes(db) as shown below.
Use our factories in hooks.server.ts (or equivalent) to inject your Drizzle instance before requests hit auth routes.
Example with Node Postgres Pool (Node runtimes):
// src/hooks.server.ts (or your server init)
import { create_pass_routes, create_session_handle } from '@drop-in/pass';
import { drizzle } from 'drizzle-orm/node-postgres';
import pg from 'pg';
import * as schema from '@drop-in/pass/schema';
import { sequence } from '@sveltejs/kit/hooks';
const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL });
const drizzleDb = drizzle(pool, { schema });
export const handle = sequence(
create_session_handle(drizzleDb),
create_pass_routes(drizzleDb)
);
Example with Cloudflare Hyperdrive (Workers):
// src/hooks.server.ts (Cloudflare Workers)
import { create_pass_routes, create_session_handle } from '@drop-in/pass';
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
import * as schema from '@drop-in/pass/schema';
import { sequence } from '@sveltejs/kit/hooks';
export const handle: Handle = async ({ event, resolve }) => {
const env = event.platform?.env as any;
const sql = postgres(env.DATABASE_URL, { prepare: true }); // via Hyperdrive
const db = drizzle(sql, { schema });
const chain = sequence(
create_session_handle(db),
create_pass_routes(db)
);
return chain({ event, resolve });
};
Example with Neon (serverless):
// src/hooks.server.ts (Neon serverless)
import { create_pass_routes, create_session_handle } from '@drop-in/pass';
import { drizzle } from 'drizzle-orm/neon-http';
import { neon } from '@neondatabase/serverless';
import * as schema from '@drop-in/pass/schema';
import { sequence } from '@sveltejs/kit/hooks';
const sql = neon(process.env.DATABASE_URL!);
const db = drizzle(sql, { schema });
export const handle = sequence(
create_session_handle(db),
create_pass_routes(db)
);
Notes:
DEBUG or NODE_ENV !== 'production'.db instance via factory functions; you control how and where Drizzle is instantiated.Password reset links are generated using create_password_link(email) and include query params: email, key (token), and expire (timestamp). The default expiration is 24 hours. The reset endpoint expects these parameters.
Configure your email provider in drop-in.config.js:
// For Cloudflare Workers with Resend
const sendEmail = async ({ to, subject, html, from }) => {
const response = await fetch('https://api.resend.com/emails', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.RESEND_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ to, subject, html, from }),
});
if (!response.ok) {
throw new Error(`Failed to send email: ${response.statusText}`);
}
};
export default {
email: {
from: 'noreply@yourdomain.com',
sendEmail,
},
app: {
url: 'https://yourdomain.com',
name: 'Your App',
route: '/dashboard'
}
};
Supported email providers:
@drop-in/beeper for Node.jsSee Email Configuration Guide for detailed examples.
Note: Signing up (POST /api/auth/register) automatically triggers a verification email in the background. The response is not delayed by email sending; failures are logged and do not block signup.
src/hooks.server.ts):import { create_pass_routes, create_session_handle } from '@drop-in/pass';
import { sequence } from '@sveltejs/kit/hooks';
import { drizzle } from 'drizzle-orm/node-postgres';
import pg from 'pg';
import * as schema from '@drop-in/pass/schema';
const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL });
const db = drizzle(pool, { schema });
export const handle = sequence(
create_session_handle(db), // Populates event.locals.user automatically
create_pass_routes(db) // Handles auth routes (/api/auth/*)
);
drop-in.config.js):export default {
email: {
from: 'noreply@yourdomain.com',
sendEmail: yourEmailFunction, // Your email implementation
},
app: {
url: 'https://yourdomain.com',
name: 'Your App Name',
route: '/dashboard'
}
};
import { pass } from '@drop-in/pass/client';
// Sign up
try {
const result = await pass.signup('user@example.com', 'securepassword');
console.log('Signed up successfully!', result.user);
} catch (error) {
console.error('Signup failed:', error.message);
}
// Login
try {
const result = await pass.login('user@example.com', 'securepassword');
console.log('Logged in successfully!', result.user);
} catch (error) {
console.error('Login failed:', error.message);
}
// Get current user
try {
const { user } = await pass.me();
console.log('Current user:', user);
} catch (error) {
console.log('Not authenticated');
}
// Logout
await pass.logout();
// In load functions, API routes, or hooks
export async function load({ locals }) {
if (locals.user) {
console.log('User is authenticated:', locals.user.id);
return {
user: locals.user
};
}
// User is not authenticated
return {};
}
// Manual authentication in API routes
import { authenticate_user } from '@drop-in/pass';
export async function GET({ cookies }) {
const auth = await authenticate_user(db, cookies);
if (!auth) {
return new Response('Unauthorized', { status: 401 });
}
// User is authenticated
console.log('User ID:', auth.user_id);
return new Response('Hello authenticated user!');
}
@drop-in/pass/client)pass.signup(email: string, password: string)Creates a new user account.
Returns: Promise<{ user: User }>
Throws: Error with validation or server error messages
pass.login(email: string, password: string)Authenticates a user.
Returns: Promise<{ user: User }>
Throws: Error with authentication failure details
pass.logout()Logs out the current user.
Returns: Promise<Response>
pass.requestPasswordReset(email: string)Requests a password reset email. Always returns success to avoid user enumeration.
Returns: Promise<Response>
pass.resetPassword(email: string, token: string, expire: number, password: string)Completes password reset. On success, sets HttpOnly cookies for JWT and refresh token.
Returns: Promise<Response>
pass.me()Gets current authenticated user information.
Returns: Promise<{ user: User }>
Throws: Error if not authenticated
authenticate_user(db: DrizzleDb, cookies: Cookies)Manually authenticate a user from cookies.
const auth = await authenticate_user(db, cookies);
if (auth) {
console.log('User ID:', auth.user_id);
}
populate_user_session(db: DrizzleDb, event: RequestEvent)Manually populate event.locals.user with authenticated user data.
await populate_user_session(db, event);
console.log(event.locals.user); // User object or undefined
The library automatically handles these routes when using create_pass_routes(db):
POST /api/auth/login - User loginPOST /api/auth/register - User registration (auto-sends verification email; non-blocking)POST /api/auth/logout - User logoutGET /api/auth/me - Get current userPOST /api/auth/verify-email - Email verificationPOST /api/auth/send-verify-email - Send verification emailPOST /api/auth/forgot-password - Request password reset (always returns success)POST /api/auth/reset-password - Complete password reset and sign in// Refresh token settings (src/cookies.ts)
export const cookie_options = {
httpOnly: true,
secure: true,
path: '/',
sameSite: 'strict' as const,
maxAge: 60 * 60 * 24 * 90, // 90 days
};
// JWT settings
export const jwt_cookie_options = {
path: '/',
maxAge: 60 * 60 * 24 * 90, // 90 days
httpOnly: true,
sameSite: 'strict' as const,
secure: true,
};
# Required
DATABASE_URL="postgresql://..."
JWT_SECRET="your-jwt-secret"
# Optional email API keys (choose one based on your provider)
RESEND_API_KEY="re_your_api_key" # For Resend
SENDGRID_API_KEY="SG.your_api_key" # For SendGrid
# MailChannels requires no API key for Cloudflare Workers
# Legacy SMTP settings (if using @drop-in/beeper)
EMAIL_HOST="smtp.gmail.com"
EMAIL_PORT="587"
EMAIL_SECURE="true"
EMAIL_USER="your-email@gmail.com"
EMAIL_PASSWORD="your-app-password"
The library includes comprehensive test coverage:
npm test # Run all tests
npm run test:watch # Watch mode
Test Coverage:
If you're upgrading from a version that used readable JWTs:
create_session_handle(db)locals.user or pass.me()See SECURITY-UPGRADE.md for detailed migration instructions.
Full TypeScript support with type definitions for:
import type { User } from '@drop-in/pass/schema';
// Event locals typing is automatic
declare global {
namespace App {
interface Locals {
user?: Partial<User>;
}
}
}
# Install dependencies
npm install
# Run tests
npm test
# Build the package
npm run build
# Development mode
npm run dev
"Not authenticated" errors in production
Database connection errors
DATABASE_URL environment variableEmail verification not working or no email received
drop-in.config.js with your sendEmail callback (signup triggers verification automatically)Runtime compatibility issues
@drop-in/beeper as your email callbackEnable debug logging:
DEBUG=drop-in:* npm run dev
We welcome contributions! Please see our contributing guidelines and:
ISC License - see LICENSE file for details.
Built with:
Made with ❤️ for the SvelteKit community
FAQs
Your drop-in season pass. aka Auth
We found that @drop-in/pass demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.