@mindmeshsystemsnpmjs/auth
v1.0.2
Published
Mindmesh Authentication Module - Prisma-based, per-app database, SMTP via .env
Maintainers
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/authruns 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 dev3. 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_FAILEDOTP_SENT/OTP_VERIFIED/OTP_FAILEDPASSWORD_RESET_REQUESTED/PASSWORD_RESET_COMPLETEDACCOUNT_BLOCKED/ACCOUNT_UNBLOCKEDEMAIL_VERIFIEDREGISTRATION_SUCCESS/REGISTRATION_FAILEDLOGOUTSESSION_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
RateLimittable
🛠️ Development
# Build
npm run build
# Watch mode
npm run dev📄 License
MIT
