rauth-provider
Version:
A lightweight, plug-and-play Node.js library for phone number authentication using the Rauth.io reverse verification flow via WhatsApp or SMS.
249 lines (219 loc) • 6.97 kB
JavaScript
const RauthProvider = require('./core/RauthProvider');
const Utils = require('./core/utils');
/**
* Server utilities for Express webhook handling
*/
class RauthServer {
/**
* Create Express middleware for body parsing with raw body preservation
* @returns {Function} Express middleware
*/
static createBodyParserMiddleware() {
return (req, res, next) => {
let data = '';
req.setEncoding('utf8');
req.on('data', (chunk) => {
data += chunk;
});
req.on('end', () => {
try {
req.body = JSON.parse(data);
req.rawBody = data;
next();
} catch (error) {
res.status(400).json({
success: false,
error: 'Invalid JSON payload'
});
}
});
};
}
/**
* Create webhook validation middleware
* @returns {Function} Express middleware
*/
static createWebhookValidationMiddleware() {
return (req, res, next) => {
const webhookSecret = req.headers['x-webhook-secret'];
if (!webhookSecret) {
return res.status(401).json({
success: false,
error: 'Missing webhook secret header'
});
}
// Get initialized config
const stats = RauthProvider.getStats();
if (!stats.initialized) {
return res.status(500).json({
success: false,
error: 'RauthProvider not initialized'
});
}
next();
};
}
/**
* Create comprehensive webhook handler with validation
* @returns {Function} Express middleware
*/
static createWebhookHandler() {
return [
this.createBodyParserMiddleware(),
this.createWebhookValidationMiddleware(),
RauthProvider.webhookHandler()
];
}
/**
* Create health check endpoint
* @returns {Function} Express middleware
*/
static createHealthCheckHandler() {
return async (req, res) => {
try {
const stats = RauthProvider.getStats();
const apiHealth = await RauthProvider.checkApiHealth();
res.status(200).json({
success: true,
message: 'RauthProvider is healthy',
stats: stats,
apiHealth: apiHealth
});
} catch (error) {
res.status(500).json({
success: false,
error: 'RauthProvider health check failed',
details: error.message
});
}
};
}
/**
* Create session initialization endpoint
* @returns {Function} Express middleware
*/
static createSessionInitHandler() {
return async (req, res) => {
try {
const { phone } = req.body;
if (!phone) {
return res.status(400).json({
success: false,
error: 'Phone number is required'
});
}
// Pass the entire headers object to initSession
const initResult = await RauthProvider.initSession(phone, req.headers);
// Forward the API response directly to the client
// New response format:
// {
// session_token: "api-generated-token",
// wa_link: "https://wa.me/918888888888?text=fhad-dfsfd-eqwt-l4dt-lueb",
// qr_image_link: "https://cdn.rauth.io/qr/15523456.png"
// }
res.status(200).json({
success: true,
message: 'Session initialized successfully',
...initResult
});
} catch (error) {
res.status(400).json({
success: false,
error: error.message
});
}
};
}
/**
* Create session verification endpoint
* @returns {Function} Express middleware
*/
static createSessionVerificationHandler() {
return async (req, res) => {
try {
const { sessionToken, userPhone } = req.body;
if (!sessionToken || !userPhone) {
return res.status(400).json({
success: false,
error: 'sessionToken and userPhone are required'
});
}
const isVerified = await RauthProvider.verifySession(sessionToken, userPhone);
if (!isVerified) {
return res.status(401).json({
success: false,
error: 'Phone number not verified or session expired'
});
}
res.status(200).json({
success: true,
message: 'Session verified successfully',
sessionToken: sessionToken,
userPhone: userPhone
});
} catch (error) {
res.status(400).json({
success: false,
error: error.message
});
}
};
}
/**
* Create session revocation check middleware
* @returns {Function} Express middleware
*/
static createRevocationCheckMiddleware() {
return async (req, res, next) => {
try {
const sessionToken = req.headers['x-session-token'] || req.body.sessionToken;
if (!sessionToken) {
return res.status(400).json({
success: false,
error: 'Session token is required'
});
}
const isRevoked = await RauthProvider.isSessionRevoked(sessionToken);
if (isRevoked) {
return res.status(401).json({
success: false,
error: 'Session has been revoked. Please log in again.'
});
}
req.sessionToken = sessionToken;
next();
} catch (error) {
res.status(500).json({
success: false,
error: 'Internal server error'
});
}
};
}
/**
* Setup complete Express routes for RauthProvider
* @param {Object} app - Express app instance
* @param {Object} options - Route options
* @param {string} [options.webhookPath='/rauth/webhook'] - Webhook endpoint path
* @param {string} [options.healthPath='/rauth/health'] - Health check endpoint path
* @param {string} [options.initPath='/rauth/init'] - Session init endpoint path
* @param {string} [options.verifyPath='/rauth/verify'] - Session verify endpoint path
*/
static setupRoutes(app, options = {}) {
const {
webhookPath = '/rauth/webhook',
healthPath = '/rauth/health',
initPath = '/rauth/init',
verifyPath = '/rauth/verify'
} = options;
// Webhook endpoint
app.post(webhookPath, this.createWebhookHandler());
// Health check endpoint
app.get(healthPath, this.createHealthCheckHandler());
// Session initialization endpoint
app.post(initPath, this.createSessionInitHandler());
// Session verification endpoint
app.post(verifyPath, this.createSessionVerificationHandler());
}
}
module.exports = RauthServer;