YeboVerify Middleware
Authentication Middleware
apiKeyAuth
Validates API key and attaches business to request.
Location: yeboverify-api/src/middleware/auth.middleware.ts
typescript
import { Request, Response, NextFunction } from 'express';
import prisma from '../config/prisma';
export interface AuthRequest extends Request {
business?: {
id: string;
name: string;
email: string;
plan: string;
};
}
export const apiKeyAuth = async (
req: AuthRequest,
res: Response,
next: NextFunction
) => {
try {
const apiKey = req.headers['x-api-key'] as string;
if (!apiKey) {
return res.status(401).json({
success: false,
error: {
code: 'MISSING_API_KEY',
message: 'API key required in X-API-Key header'
}
});
}
const business = await prisma.business.findUnique({
where: { apiKey }
});
if (!business) {
return res.status(401).json({
success: false,
error: {
code: 'INVALID_API_KEY',
message: 'Invalid API key'
}
});
}
if (!business.isActive) {
return res.status(401).json({
success: false,
error: {
code: 'BUSINESS_INACTIVE',
message: 'Business account is inactive'
}
});
}
req.business = {
id: business.id,
name: business.name,
email: business.email,
plan: business.plan
};
next();
} catch (error) {
return res.status(500).json({
success: false,
error: {
code: 'AUTH_ERROR',
message: 'Authentication failed'
}
});
}
};adminAuth
Admin-only routes (for business registration).
typescript
export const adminAuth = (
req: Request,
res: Response,
next: NextFunction
) => {
const adminKey = req.headers['x-admin-key'] as string;
if (adminKey !== process.env.ADMIN_API_KEY) {
return res.status(403).json({
success: false,
error: {
code: 'FORBIDDEN',
message: 'Admin access required'
}
});
}
next();
};Upload Middleware
Location: yeboverify-api/src/middleware/upload.middleware.ts
Handles multipart form uploads using Multer.
typescript
import multer from 'multer';
const storage = multer.memoryStorage();
const fileFilter = (
req: Request,
file: Express.Multer.File,
cb: multer.FileFilterCallback
) => {
const allowedTypes = [
'image/jpeg',
'image/jpg',
'image/png',
'image/webp',
'image/heic',
'image/heif'
];
if (allowedTypes.includes(file.mimetype)) {
cb(null, true);
} else {
cb(new Error(`Invalid file type: ${file.mimetype}`));
}
};
export const upload = multer({
storage,
fileFilter,
limits: {
fileSize: 10 * 1024 * 1024, // 10MB
files: 3 // max 3 files
}
});
// Usage in routes
router.post(
'/verify',
apiKeyAuth,
upload.fields([
{ name: 'idFront', maxCount: 1 },
{ name: 'idBack', maxCount: 1 },
{ name: 'selfie', maxCount: 1 },
]),
submitVerification
);Error Handler
typescript
app.use((
err: Error,
req: Request,
res: Response,
next: NextFunction
) => {
console.error('Error:', err);
// Multer errors
if (err instanceof multer.MulterError) {
if (err.code === 'LIMIT_FILE_SIZE') {
return res.status(400).json({
success: false,
error: {
code: 'FILE_TOO_LARGE',
message: 'File size exceeds 10MB limit'
}
});
}
}
// Custom file filter errors
if (err.message.includes('Invalid file type')) {
return res.status(400).json({
success: false,
error: {
code: 'INVALID_FILE_TYPE',
message: err.message
}
});
}
// Default error
res.status(500).json({
success: false,
error: {
code: 'INTERNAL_ERROR',
message: process.env.NODE_ENV === 'production'
? 'Internal server error'
: err.message
}
});
});CORS
typescript
import cors from 'cors';
app.use(cors({
origin: [
'https://dashboard.yeboverify.com',
'https://yeboverify.com'
],
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'X-API-Key', 'X-Admin-Key']
}));