import { Request, Response, NextFunction } from 'express'; import jwt from 'jsonwebtoken'; import { loadSettings } from '../config/index.js'; import defaultConfig from '../config/index.js'; import { JWT_SECRET } from '../config/jwt.js'; import { getToken } from '../models/OAuth.js'; import { isOAuthServerEnabled } from '../services/oauthServerService.js'; import { getBearerKeyDao } from '../dao/index.js'; import { BearerKey } from '../types/index.js'; const validateBearerAuth = async (req: Request): Promise => { const bearerKeyDao = getBearerKeyDao(); const enabledKeys = await bearerKeyDao.findEnabled(); // If there are no enabled keys, bearer auth via static keys is disabled if (enabledKeys.length === 0) { return false; } const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { return false; } const token = authHeader.substring(7).trim(); if (!token) { return false; } const matchingKey: BearerKey | undefined = enabledKeys.find((key) => key.token === token); if (!matchingKey) { console.warn('Bearer auth failed: token did not match any configured bearer key'); return false; } console.log( `Bearer auth succeeded with key id=${matchingKey.id}, name=${matchingKey.name}, accessType=${matchingKey.accessType}`, ); return true; }; const readonlyAllowPaths = ['/tools/call/']; const checkReadonly = (req: Request): boolean => { if (!defaultConfig.readonly) { return true; } for (const path of readonlyAllowPaths) { if (req.path.startsWith(defaultConfig.basePath + path)) { return true; } } return req.method === 'GET'; }; // Middleware to authenticate JWT token export const auth = async (req: Request, res: Response, next: NextFunction): Promise => { const t = (req as any).t; if (!checkReadonly(req)) { res.status(403).json({ success: false, message: t('api.errors.readonly') }); return; } // Check if authentication is disabled globally const routingConfig = loadSettings().systemConfig?.routing || { enableGlobalRoute: true, enableGroupNameRoute: true, skipAuth: false, }; if (routingConfig.skipAuth) { next(); return; } // Check if bearer auth via configured keys can validate this request if (await validateBearerAuth(req)) { next(); return; } // Check for OAuth access token in Authorization header const authHeader = req.headers.authorization; if (authHeader && authHeader.startsWith('Bearer ') && isOAuthServerEnabled()) { const accessToken = authHeader.substring(7); const oauthToken = await getToken(accessToken); if (oauthToken && oauthToken.accessToken === accessToken) { // Valid OAuth token - look up user to get admin status const { findUserByUsername } = await import('../models/User.js'); const user = await findUserByUsername(oauthToken.username); // Set user context with proper admin status (req as any).user = { username: oauthToken.username, isAdmin: user?.isAdmin || false, }; (req as any).oauthToken = oauthToken; next(); return; } } // Get token from header or query parameter const headerToken = req.header('x-auth-token'); const queryToken = req.query.token as string; const token = headerToken || queryToken; // Check if no token if (!token) { res.status(401).json({ success: false, message: 'No token, authorization denied' }); return; } // Verify JWT token try { const decoded = jwt.verify(token, JWT_SECRET); // Add user from payload to request (req as any).user = (decoded as any).user; next(); } catch (error) { res.status(401).json({ success: false, message: 'Token is not valid' }); } };