Skip to content

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']
}));

One chat. Everything done.