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

@mindmeshsystemsnpmjs/auth

v1.0.2

Published

Mindmesh Authentication Module - Prisma-based, per-app database, SMTP via .env

Readme

@mindmesh/auth

Mindmesh Authentication Module - A Prisma-based, per-app database authentication system with OTP verification, SMTP email support, and comprehensive security features.

🧠 Core Philosophy

  • Every Mindmesh app has its own database
  • @mindmesh/auth runs inside that database
  • Prisma schema lives in the main application
  • Auth package assumes the schema exists
  • SMTP credentials come from the app's .env
  • Users table is extensible by apps

📦 Installation

npm install @mindmesh/auth

🔧 Setup

1. Add Prisma Schema

Copy the contents of prisma/auth.prisma into your main schema.prisma file, or import it:

// schema.prisma
import "./auth.prisma"

2. Run Prisma Migrations

npx prisma migrate dev

3. Environment Variables

Add these to your .env file:

AUTH_JWT_SECRET=your-secret-key-here
AUTH_REFRESH_SECRET=your-refresh-secret-here

SMTP_HOST=smtp.office365.com
SMTP_PORT=587
[email protected]
SMTP_PASS=your-password
SMTP_FROM="Mindmesh Security <[email protected]>"

OTP_EXPIRY_MINUTES=5
SESSION_DAYS=7

🚀 Usage

Basic Setup

import { PrismaClient } from '@prisma/client';
import MindmeshAuth from '@mindmesh/auth';

const prisma = new PrismaClient();

// Initialize auth
const auth = new MindmeshAuth({
  prisma,
  // Optional: override config
  // config: { ... },
  // Optional: set request context
  // ipAddress: req.ip,
  // userAgent: req.headers['user-agent'],
});

Authentication Flow

1. Register a User

try {
  const user = await auth.auth.register('[email protected]', 'password123');
  console.log('User registered:', user.id);
} catch (error) {
  console.error('Registration failed:', error.message);
}

2. Login (Step 1: Request OTP)

try {
  const result = await auth.auth.login('[email protected]', 'password123');
  // Returns: { otpRequired: true }
  // OTP is sent via email
} catch (error) {
  console.error('Login failed:', error.message);
}

3. Verify OTP (Step 2: Complete Login)

try {
  const result = await auth.auth.verifyOtp('[email protected]', '123456', 'login');
  // Returns: { accessToken, refreshToken, user }
  
  // Store tokens securely
  res.cookie('accessToken', result.accessToken, { httpOnly: true });
  res.cookie('refreshToken', result.refreshToken, { httpOnly: true });
} catch (error) {
  console.error('OTP verification failed:', error.message);
}

4. Get User from Token

try {
  const user = await auth.auth.getUserFromToken(accessToken);
  console.log('Authenticated user:', user.email);
} catch (error) {
  console.error('Token invalid:', error.message);
}

5. Refresh Token

try {
  const tokens = await auth.auth.refresh(refreshToken);
  // Returns: { accessToken, refreshToken }
} catch (error) {
  console.error('Token refresh failed:', error.message);
}

6. Logout

// Extract sessionId from JWT token
const payload = jwt.decode(accessToken);
await auth.auth.logout(payload.sessionId);

OTP Module

// Send OTP
await auth.otp.send('[email protected]', 'email_verify');

// Verify OTP
const isValid = await auth.otp.verify('[email protected]', '123456', 'email_verify');

Password Reset

// Request password reset
await auth.password.requestReset('[email protected]');
// Token is sent via email

// Reset password with token
await auth.password.reset(resetToken, 'newPassword123');

User Management

// Get user by ID
const user = await auth.user.getById(userId);

// Block user
await auth.user.block(userId);

// Unblock user
await auth.user.unblock(userId);

// Mark email as verified
await auth.user.markEmailVerified(userId);

🔐 Security Features

Rate Limiting

| Action | Limit | | ---------- | ------------ | | OTP send | 3 per 10 min | | OTP verify | 5 tries | | Login | 10 per IP | | Reset | 3 per hour |

OTP Rules

  • Expires in 5 minutes (configurable)
  • Maximum 5 attempts
  • One-time use only
  • Stored as bcrypt hash
  • Rate limited per email

Token System

  • Access JWT: 15-minute expiry, contains userId, sessionId, email
  • Refresh Token: Random 128-character string, stored in database
  • Sessions can be revoked
  • Automatic session cleanup on account block

Audit Logging

All sensitive actions are logged to AuthEvent table:

  • LOGIN_SUCCESS / LOGIN_FAILED
  • OTP_SENT / OTP_VERIFIED / OTP_FAILED
  • PASSWORD_RESET_REQUESTED / PASSWORD_RESET_COMPLETED
  • ACCOUNT_BLOCKED / ACCOUNT_UNBLOCKED
  • EMAIL_VERIFIED
  • REGISTRATION_SUCCESS / REGISTRATION_FAILED
  • LOGOUT
  • SESSION_REFRESHED

🎯 Express.js Integration Example

import express from 'express';
import { PrismaClient } from '@prisma/client';
import MindmeshAuth from '@mindmesh/auth';

const app = express();
const prisma = new PrismaClient();
const auth = new MindmeshAuth({ prisma });

app.use(express.json());

// Login endpoint
app.post('/api/auth/login', async (req, res) => {
  try {
    auth.setContext({
      ipAddress: req.ip,
      userAgent: req.headers['user-agent'],
    });
    
    const result = await auth.auth.login(req.body.email, req.body.password);
    res.json(result);
  } catch (error) {
    res.status(401).json({ error: error.message });
  }
});

// Verify OTP endpoint
app.post('/api/auth/verify-otp', async (req, res) => {
  try {
    auth.setContext({
      ipAddress: req.ip,
      userAgent: req.headers['user-agent'],
    });
    
    const result = await auth.auth.verifyOtp(
      req.body.email,
      req.body.otp,
      'login'
    );
    res.json(result);
  } catch (error) {
    res.status(401).json({ error: error.message });
  }
});

// Protected route middleware
async function requireAuth(req: express.Request, res: express.Response, next: express.NextFunction) {
  const token = req.headers.authorization?.replace('Bearer ', '');
  
  if (!token) {
    return res.status(401).json({ error: 'No token provided' });
  }
  
  try {
    const user = await auth.auth.getUserFromToken(token);
    (req as any).user = user;
    next();
  } catch (error) {
    res.status(401).json({ error: 'Invalid token' });
  }
}

app.get('/api/profile', requireAuth, async (req, res) => {
  res.json({ user: (req as any).user });
});

🔄 Extending the User Model

Apps can extend the User model by adding fields in their Prisma schema:

model User {
  id              String   @id @default(uuid())
  email           String   @unique
  passwordHash    String
  // ... auth fields ...
  
  // Your custom fields
  firstName       String?
  lastName        String?
  role            String   @default("user")
  tenantId        String?
  profile         Json?
  
  // Your relations
  posts           Post[]
}

📝 Notes

  • The auth module assumes the Prisma schema is already set up
  • All passwords and OTPs are hashed using bcrypt
  • Refresh tokens are stored in the database for revocation
  • Email templates can be customized in src/utils/email.ts
  • Rate limiting is stored in the RateLimit table

🛠️ Development

# Build
npm run build

# Watch mode
npm run dev

📄 License

MIT