YeboSafe Middleware
Authentication Middleware
Location: yebosafe-api/src/middleware/auth.ts
flexAuth
Flexible authentication supporting both API key and JWT.
typescript
import { Request, Response, NextFunction } from 'express';
import prisma from '../config/prisma';
import jwt from 'jsonwebtoken';
export interface AuthRequest extends Request {
merchant?: {
id: string;
email: string;
name: string;
};
}
export const flexAuth = async (
req: AuthRequest,
res: Response,
next: NextFunction
) => {
try {
const authHeader = req.headers.authorization;
if (!authHeader?.startsWith('Bearer ')) {
return res.status(401).json({
success: false,
error: { code: 'UNAUTHORIZED', message: 'No token provided' }
});
}
const token = authHeader.substring(7);
// Try API key first (starts with sk_)
if (token.startsWith('sk_')) {
const apiKey = await prisma.apiKey.findUnique({
where: { key: token },
include: { merchant: true }
});
if (!apiKey || !apiKey.isActive) {
return res.status(401).json({
success: false,
error: { code: 'INVALID_API_KEY', message: 'Invalid API key' }
});
}
if (!apiKey.merchant.isActive) {
return res.status(401).json({
success: false,
error: { code: 'MERCHANT_INACTIVE', message: 'Merchant account inactive' }
});
}
// Update last used
await prisma.apiKey.update({
where: { id: apiKey.id },
data: { lastUsedAt: new Date() }
});
req.merchant = {
id: apiKey.merchant.id,
email: apiKey.merchant.email,
name: apiKey.merchant.name
};
return next();
}
// Try JWT
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET!) as {
merchantId: string;
};
const merchant = await prisma.merchant.findUnique({
where: { id: decoded.merchantId }
});
if (!merchant || !merchant.isActive) {
return res.status(401).json({
success: false,
error: { code: 'INVALID_TOKEN', message: 'Invalid token' }
});
}
req.merchant = {
id: merchant.id,
email: merchant.email,
name: merchant.name
};
return next();
} catch {
return res.status(401).json({
success: false,
error: { code: 'INVALID_TOKEN', message: 'Invalid or expired token' }
});
}
} catch (error) {
return res.status(500).json({
success: false,
error: { code: 'AUTH_ERROR', message: 'Authentication failed' }
});
}
};Request Validation
Location: yebosafe-api/src/routes/escrow.routes.ts
Using express-validator:
typescript
import { body } from 'express-validator';
// Create escrow validation
router.post(
'/',
[
body('amount')
.isFloat({ gt: 0 })
.withMessage('Amount must be positive'),
body('currency')
.optional()
.isLength({ min: 3, max: 3 }),
body('description')
.optional()
.isString()
.isLength({ max: 500 }),
],
createEscrow
);
// Refuse escrow validation
router.post(
'/:id/refuse',
[
body('reason')
.notEmpty()
.withMessage('Reason is required')
],
refuseEscrow
);
// Complete escrow validation
router.post(
'/:id/complete',
[
body('completionCode')
.notEmpty()
.withMessage('Completion code is required')
],
completeEscrow
);Error Handling
typescript
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
console.error('Error:', err);
res.status(500).json({
success: false,
error: {
code: 'INTERNAL_ERROR',
message: process.env.NODE_ENV === 'production'
? 'Internal server error'
: err.message
}
});
});CORS Configuration
typescript
import cors from 'cors';
app.use(cors({
origin: [
'https://dashboard.yebosafe.com',
'https://yebosafe.com',
'http://localhost:3000' // Development
],
credentials: true
}));