@re-auth/http-adapters
Version:
HTTP adapters for ReAuth authentication framework
243 lines • 9.34 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExpressAuthAdapter = void 0;
exports.createExpressAdapter = createExpressAdapter;
const express_1 = require("express");
class ExpressAuthAdapter {
constructor(engine, config = {}) {
this.engine = engine;
this.config = {
path: '/auth',
cookieName: 'auth_token',
cookieOptions: {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 60 * 60 * 24 * 7 * 1000, // 7 days in ms
},
...config,
};
this.router = (0, express_1.Router)();
this.setupMiddleware();
this.setupRoutes();
}
setupMiddleware() {
// Parse cookies
this.router.use((req, res, next) => {
if (req.cookies)
return next();
// Simple cookie parsing if no cookie-parser is used
const cookieHeader = req.headers.cookie || '';
req.cookies = cookieHeader
.split(';')
.reduce((cookies, cookie) => {
const [name, value] = cookie.trim().split('=');
cookies[name] = value;
return cookies;
}, {});
next();
});
// Auth middleware
this.router.use(async (req, res, next) => {
const token = this.extractToken(req);
if (token) {
try {
const session = await this.engine.checkSession(token);
if (session.valid && session.entity) {
req.user = session.entity;
req.token = session.token;
req.isAuthenticated = () => true;
}
}
catch (error) {
console.warn('Invalid token:', error);
}
}
if (!req.isAuthenticated) {
req.isAuthenticated = () => false;
}
next();
});
}
setupRoutes() {
const plugins = this.engine.getAllPlugins();
plugins.forEach((plugin) => {
plugin.steps.forEach((step) => {
if (!step.protocol?.http)
return;
const path = `${this.config.path}/${plugin.name}/${step.name}`;
const method = step.protocol.http.method.toLowerCase();
// Create route handler
const handler = this.createStepHandler(plugin.name, step.name, step.protocol.http);
// Add auth middleware if required
const middlewares = [];
if (step.protocol.http.auth) {
middlewares.push(this.requireAuth());
}
// Register route
this.router[method](path, ...middlewares, handler);
});
});
}
createStepHandler(pluginName, stepName, httpConfig) {
return async (req, res, next) => {
try {
// Extract inputs from request
const inputs = await this.extractInputs(req, pluginName, stepName);
// Add auth context if authenticated
if (req.isAuthenticated()) {
inputs.entity = req.user;
inputs.token = req.token;
}
// Execute the step
const result = await this.engine.executeStep(pluginName, stepName, inputs);
// Handle the response
return this.handleStepResponse(req, res, result, httpConfig);
}
catch (error) {
console.error(`Error in ${pluginName}.${stepName}:`, error);
this.errorResponse(res, error);
}
};
}
async extractInputs(req, pluginName, stepName) {
const expectedInputs = this.engine.getStepInputs(pluginName, stepName);
const inputs = {};
// Extract from body, query, and params
expectedInputs.forEach((inputName) => {
if (req.body && req.body[inputName] !== undefined) {
inputs[inputName] = req.body[inputName];
}
else if (req.query[inputName] !== undefined) {
inputs[inputName] = req.query[inputName];
}
else if (req.params[inputName] !== undefined) {
inputs[inputName] = req.params[inputName];
}
});
// Validate required inputs
const missingInputs = expectedInputs.filter((input) => inputs[input] === undefined ||
inputs[input] === null ||
inputs[input] === '');
if (missingInputs.length > 0) {
throw new Error(`Missing required inputs: ${missingInputs.join(', ')}`);
}
return inputs;
}
handleStepResponse(req, res, result, httpConfig) {
const { token, redirect, success, status, ...data } = result;
// Handle token (set cookie)
if (token) {
res.cookie(this.config.cookieName, token, this.config.cookieOptions);
}
//TODO: this stills need digging, Might not be correct or be a bug
// Handle logout (clear cookie)
if (status === 'logged_out' || (success && !token && req.token)) {
res.clearCookie(this.config.cookieName, this.config.cookieOptions);
}
// Handle redirect
if (redirect) {
res.redirect(redirect);
}
// Determine status code
const statusCode = this.getStatusCode(result, httpConfig);
// Return JSON response
res.status(statusCode).json({
success,
...data,
});
}
getStatusCode(result, httpConfig) {
// Check if step defines custom status codes
if (httpConfig && httpConfig[result.status]) {
return httpConfig[result.status];
}
// Default status codes
if (!result.success) {
return result.status === 'unauthorized' ? 401 : 400;
}
return result.status === 'created' ? 201 : 200;
}
errorResponse(res, error) {
console.error('Auth error:', error);
if (error.name === 'InputValidationError') {
return res.status(400).json({
success: false,
error: 'Validation Error',
message: error.message,
});
}
return res.status(500).json({
success: false,
error: 'Internal Server Error',
message: process.env.NODE_ENV === 'development' ? error.message : undefined,
});
}
requireAuth() {
return (req, res, next) => {
if (req.isAuthenticated()) {
return next();
}
res.status(401).json({ success: false, error: 'Unauthorized' });
};
}
extractToken(req) {
// 1. Check Authorization header
const authHeader = req.headers.authorization;
if (authHeader && authHeader.startsWith('Bearer ')) {
return authHeader.substring(7);
}
// 2. Check cookies
if (req.cookies?.[this.config.cookieName]) {
return req.cookies[this.config.cookieName];
}
// // 3. Check query parameter
// if (req.query?.token && typeof req.query.token === 'string') {
// return req.query.token;
// }
return null;
}
getRouter() {
return this.router;
}
protect(options = {}) {
return async (req, res, next) => {
const entity = req.user;
if (!entity) {
return res.status(401).json({ success: false, error: 'Unauthorized' });
}
if (options.roles && !options.roles.includes(entity.role)) {
return res.status(403).json({ success: false, error: 'Forbidden' });
}
if (options.authorize) {
const authorized = await options.authorize(entity, req, res, next);
if (!authorized) {
return res.status(403).json({ success: false, error: 'Forbidden' });
}
}
next();
};
}
}
exports.ExpressAuthAdapter = ExpressAuthAdapter;
function createExpressAdapter(engine, config) {
const adapter = new ExpressAuthAdapter(engine, config);
return adapter.getRouter();
}
exports.default = createExpressAdapter;
__exportStar(require("./express-adapter-v2"), exports);
//# sourceMappingURL=index.js.map