- Added specific error reasons for auth failures: no_auth_header, invalid_auth_format, invalid_token - Fixed AUTH_TOKEN_FILE support in Docker production stacks (issue #16) - Added AUTH_TOKEN_FILE support to single-session HTTP server for consistency - Enhanced security by removing token lengths from logs - Added token trimming and empty token validation - Updated Docker entrypoint to properly support AUTH_TOKEN_FILE - Bumped version to 2.7.10 This improves debugging for mcp-remote authentication issues and enables proper Docker secrets usage in production environments. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -53,14 +53,17 @@ function validateEnvironment() {
|
||||
// Load auth token from env var or file
|
||||
authToken = loadAuthToken();
|
||||
|
||||
if (!authToken) {
|
||||
logger.error('No authentication token found');
|
||||
console.error('ERROR: AUTH_TOKEN is required for HTTP mode');
|
||||
if (!authToken || authToken.trim() === '') {
|
||||
logger.error('No authentication token found or token is empty');
|
||||
console.error('ERROR: AUTH_TOKEN is required for HTTP mode and cannot be empty');
|
||||
console.error('Set AUTH_TOKEN environment variable or AUTH_TOKEN_FILE pointing to a file containing the token');
|
||||
console.error('Generate AUTH_TOKEN with: openssl rand -base64 32');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Update authToken to trimmed version
|
||||
authToken = authToken.trim();
|
||||
|
||||
if (authToken.length < 32) {
|
||||
logger.warn('AUTH_TOKEN should be at least 32 characters for security');
|
||||
console.warn('WARNING: AUTH_TOKEN should be at least 32 characters for security');
|
||||
@@ -182,16 +185,55 @@ export async function startFixedHTTPServer() {
|
||||
app.post('/mcp', async (req: express.Request, res: express.Response): Promise<void> => {
|
||||
const startTime = Date.now();
|
||||
|
||||
// Simple auth check
|
||||
// Enhanced authentication check with specific logging
|
||||
const authHeader = req.headers.authorization;
|
||||
const token = authHeader?.startsWith('Bearer ')
|
||||
? authHeader.slice(7)
|
||||
: authHeader;
|
||||
|
||||
if (token !== authToken) {
|
||||
logger.warn('Authentication failed', {
|
||||
// Check if Authorization header is missing
|
||||
if (!authHeader) {
|
||||
logger.warn('Authentication failed: Missing Authorization header', {
|
||||
ip: req.ip,
|
||||
userAgent: req.get('user-agent')
|
||||
userAgent: req.get('user-agent'),
|
||||
reason: 'no_auth_header'
|
||||
});
|
||||
res.status(401).json({
|
||||
jsonrpc: '2.0',
|
||||
error: {
|
||||
code: -32001,
|
||||
message: 'Unauthorized'
|
||||
},
|
||||
id: null
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if Authorization header has Bearer prefix
|
||||
if (!authHeader.startsWith('Bearer ')) {
|
||||
logger.warn('Authentication failed: Invalid Authorization header format (expected Bearer token)', {
|
||||
ip: req.ip,
|
||||
userAgent: req.get('user-agent'),
|
||||
reason: 'invalid_auth_format',
|
||||
headerPrefix: authHeader.substring(0, 10) + '...' // Log first 10 chars for debugging
|
||||
});
|
||||
res.status(401).json({
|
||||
jsonrpc: '2.0',
|
||||
error: {
|
||||
code: -32001,
|
||||
message: 'Unauthorized'
|
||||
},
|
||||
id: null
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract token and trim whitespace
|
||||
const token = authHeader.slice(7).trim();
|
||||
|
||||
// Check if token matches
|
||||
if (token !== authToken) {
|
||||
logger.warn('Authentication failed: Invalid token', {
|
||||
ip: req.ip,
|
||||
userAgent: req.get('user-agent'),
|
||||
reason: 'invalid_token'
|
||||
});
|
||||
res.status(401).json({
|
||||
jsonrpc: '2.0',
|
||||
|
||||
Reference in New Issue
Block a user