npm package: @mindmeshsystemsnpmjs/auth
Description: Mindmesh Authentication Module - Prisma-based, per-app database, SMTP via .env
Latest version: 1.0.2

# @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

```bash
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:

```prisma
// schema.prisma
import "./auth.prisma"
```

### 2. Run Prisma Migrations

```bash
npx prisma migrate dev
```

### 3. Environment Variables

Add these to your `.env` file:

```env
AUTH_JWT_SECRET=your-secret-key-here
AUTH_REFRESH_SECRET=your-refresh-secret-here

SMTP_HOST=smtp.office365.com
SMTP_PORT=587
SMTP_USER=your-email@domain.com
SMTP_PASS=your-password
SMTP_FROM="Mindmesh Security <no-reply@mindmesh.com>"

OTP_EXPIRY_MINUTES=5
SESSION_DAYS=7
```

## 🚀 Usage

### Basic Setup

```typescript
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

```typescript
try {
  const user = await auth.auth.register('user@example.com', 'password123');
  console.log('User registered:', user.id);
} catch (error) {
  console.error('Registration failed:', error.message);
}
```

#### 2. Login (Step 1: Request OTP)

```typescript
try {
  const result = await auth.auth.login('user@example.com', 'password123');
  // Returns: { otpRequired: true }
  // OTP is sent via email
} catch (error) {
  console.error('Login failed:', error.message);
}
```

#### 3. Verify OTP (Step 2: Complete Login)

```typescript
try {
  const result = await auth.auth.verifyOtp('user@example.com', '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

```typescript
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

```typescript
try {
  const tokens = await auth.auth.refresh(refreshToken);
  // Returns: { accessToken, refreshToken }
} catch (error) {
  console.error('Token refresh failed:', error.message);
}
```

#### 6. Logout

```typescript
// Extract sessionId from JWT token
const payload = jwt.decode(accessToken);
await auth.auth.logout(payload.sessionId);
```

### OTP Module

```typescript
// Send OTP
await auth.otp.send('user@example.com', 'email_verify');

// Verify OTP
const isValid = await auth.otp.verify('user@example.com', '123456', 'email_verify');
```

### Password Reset

```typescript
// Request password reset
await auth.password.requestReset('user@example.com');
// Token is sent via email

// Reset password with token
await auth.password.reset(resetToken, 'newPassword123');
```

### User Management

```typescript
// 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

```typescript
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:

```prisma
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

```bash
# Build
npm run build

# Watch mode
npm run dev
```

## 📄 License

MIT