Browser/Terminal Access Control
XyPriss provides sophisticated access control middleware that can restrict endpoints to specific client types. This allows you to create browser-only APIs, terminal-only endpoints, or mixed configurations based on how requests are made.
Overview
Two specialized middleware components allow you to control access based on the request source:
- BrowserOnly: Blocks requests from non-browser clients (cURL, Postman, scripts, etc.)
- TerminalOnly: Blocks requests from browsers, allowing only terminal-based clients
Browser-Only Middleware
Use Cases
- Web Application APIs: Ensure only your frontend can access certain endpoints
- CSRF Protection: Complement existing CSRF protection
- User Session Validation: Require browser session cookies
- Analytics Endpoints: Prevent automated access to analytics data
Quick Start
import { createServer } from 'xypriss';
const server = createServer({
security: {
browserOnly: true // Enable globally
}
});
// This endpoint only accepts browser requests
server.get('/api/user/profile', (req, res) => {
res.json({
message: 'Profile data for browser users only',
userAgent: req.headers['user-agent']
});
});
server.start();
Configuration Options
interface BrowserOnlyConfig {
/** Enable browser-only access control */
enabled?: boolean;
/** Custom error message */
message?: string;
/** HTTP status code for blocked requests */
statusCode?: number;
/** Additional user agent patterns to allow */
allowedUserAgents?: RegExp[];
/** User agent patterns to block */
blockedUserAgents?: RegExp[];
/** Require specific headers that browsers typically send */
requireHeaders?: string[];
/** Allow requests missing typical browser headers */
allowMissingHeaders?: boolean;
/** Custom validation function */
customValidator?: (req: Request) => boolean;
}
Advanced Configuration
const server = createServer({
security: {
browserOnly: {
enabled: true,
message: 'This endpoint requires a web browser',
statusCode: 403,
allowedUserAgents: [
/CustomBrowser\/\d+\.\d+/ // Allow custom browser
],
blockedUserAgents: [
/Bot\/\d+\.\d+/, // Block bots
/Crawler\/\d+\.\d+/ // Block crawlers
],
requireHeaders: [
'accept',
'accept-language',
'cache-control'
],
customValidator: (req) => {
// Custom validation logic
return req.headers['x-custom-header'] === 'expected-value';
}
}
}
});
Terminal-Only Middleware
Use Cases
- API Endpoints: Expose APIs only for programmatic access
- CLI Tools: Create endpoints specifically for command-line tools
- Internal APIs: Restrict access to server-to-server communication
- Admin Operations: Allow only terminal-based admin access
Quick Start
const server = createServer({
security: {
terminalOnly: true // Enable globally
}
});
// This endpoint only accepts terminal requests (cURL, scripts, etc.)
server.post('/api/admin/reset-database', (req, res) => {
// Dangerous operation - only allow from terminal
res.json({ message: 'Database reset complete' });
});
Configuration Options
interface TerminalOnlyConfig {
/** Enable terminal-only access control */
enabled?: boolean;
/** Custom error message */
message?: string;
/** HTTP status code for blocked requests */
statusCode?: number;
/** Allowed user agent patterns */
allowedUserAgents?: RegExp[];
/** Blocked user agent patterns */
blockedUserAgents?: RegExp[];
/** Require absence of browser-specific headers */
blockBrowserHeaders?: string[];
/** Allow requests with browser-like headers */
allowBrowserHeaders?: boolean;
/** Custom validation function */
customValidator?: (req: Request) => boolean;
}
Advanced Configuration
const server = createServer({
security: {
terminalOnly: {
enabled: true,
message: 'This endpoint is for terminal access only',
statusCode: 403,
allowedUserAgents: [
/curl\/\d+\.\d+/, // Allow cURL
/HTTPie\/\d+\.\d+/, // Allow HTTPie
/PostmanRuntime\/\d+\.\d+/, // Allow Postman
/MyCLITool\/\d+\.\d+/ // Allow custom CLI tool
],
blockedUserAgents: [
/Mozilla\/\d+\.\d+/, // Block browsers
/Chrome\/\d+\.\d+/, // Block Chrome
/Safari\/\d+\.\d+/ // Block Safari
],
blockBrowserHeaders: [
'referer',
'sec-fetch-mode',
'sec-fetch-site'
],
customValidator: (req) => {
// Custom validation - check for API key
return req.headers['x-api-key'] === process.env.API_KEY;
}
}
}
});
Detection Logic
Browser Detection
The BrowserOnly middleware identifies browsers by:
-
User Agent Analysis
// Common browser patterns const browserPatterns = [ /Mozilla\/\d+\.\d+/, /Chrome\/\d+\.\d+/, /Safari\/\d+\.\d+/, /Firefox\/\d+\.\d+/, /Edge\/\d+\.\d+/ ]; -
Browser-Specific Headers
// Headers that browsers typically send const browserHeaders = [ 'accept', 'accept-language', 'accept-encoding', 'cache-control', 'pragma', 'sec-fetch-mode', 'sec-fetch-site', 'sec-fetch-user', 'sec-fetch-dest', 'sec-ch-ua', 'sec-ch-ua-mobile', 'sec-ch-ua-platform' ]; -
Request Characteristics
- Presence of cookies
- Referer header
- DNT (Do Not Track) header
- Upgrade-Insecure-Requests header
Terminal Detection
The TerminalOnly middleware identifies terminal clients by:
-
User Agent Analysis
// Common terminal tool patterns const terminalPatterns = [ /curl\/\d+\.\d+/, /wget\/\d+\.\d+/, /HTTPie\/\d+\.\d+/, /PostmanRuntime\/\d+\.\d+/, /python-requests\/\d+\.\d+/, /axios\/\d+\.\d+/, /node-fetch\/\d+\.\d+/ ]; -
Absence of Browser Headers
- No
sec-fetch-*headers - No
sec-ch-ua*headers - No browser-specific user agent
- No
-
Request Characteristics
- Simple Accept header (
*/*orapplication/json) - Presence of custom API headers
- No cookies (typically)
- Simple Accept header (
Route-Specific Configuration
Apply access control to specific routes:
const server = createServer({
security: {
browserOnly: false, // Disabled globally
terminalOnly: false // Disabled globally
}
});
// Browser-only route
server.get('/app/dashboard', (req, res) => {
// Only accessible from browsers
res.json({ dashboard: 'data' });
});
// Terminal-only route
server.post('/api/admin/backup', (req, res) => {
// Only accessible from terminals
res.json({ backup: 'started' });
});
// Mixed access route
server.get('/api/public', (req, res) => {
// Accessible from both browsers and terminals
res.json({ public: 'data' });
});
Integration with Other Security
Combined with Request Signatures
const server = createServer({
security: {
// Browser endpoints require session validation
browserOnly: {
enabled: true,
requireHeaders: ['cookie', 'authorization']
},
// API endpoints require signature authentication
terminalOnly: {
enabled: true,
customValidator: (req) => {
return req.headers['x-api-signature'] !== undefined;
}
},
// Request signature for additional security
requestSignature: {
secret: 'api-secret-key'
}
}
});
Combined with CORS
const server = createServer({
security: {
cors: {
origin: [
"https://myapp.com", // Allow browser access
/^localhost:\d+$/ // Allow development
],
credentials: true
},
// Additional browser validation
browserOnly: {
enabled: true,
requireHeaders: ['sec-fetch-site']
}
}
});
Practical Examples
Web Application with API
const server = createServer({
security: {
// Web app routes - browser only
browserOnly: {
enabled: true,
customValidator: (req) => {
// Check for session cookie
return req.headers.cookie?.includes('session_id');
}
}
}
});
// Web app routes
server.get('/app/*', (req, res) => {
res.sendFile('index.html'); // Serve SPA
});
server.get('/api/user/profile', (req, res) => {
// Browser-only API
res.json({ profile: getUserProfile(req) });
});
// Public API routes (override browser-only)
server.get('/api/public/stats', (req, res) => {
// Allow both browser and terminal access
res.json({ stats: getPublicStats() });
});
CLI Tool API
const server = createServer({
security: {
// CLI tool routes - terminal only
terminalOnly: {
enabled: true,
allowedUserAgents: [
/my-cli-tool\/\d+\.\d+/,
/curl\/\d+\.\d+/
]
}
}
});
// CLI-only endpoints
server.post('/cli/deploy', (req, res) => {
// Only accessible from CLI tool
const result = deployApplication(req.body);
res.json({ deployment: result });
});
server.get('/cli/status', (req, res) => {
// Check deployment status
res.json({ status: getDeploymentStatus() });
});
Admin Panel
const server = createServer({
security: {
browserOnly: {
// Admin panel - browser only with additional checks
enabled: true,
customValidator: (req) => {
const cookies = parseCookies(req.headers.cookie || '');
return cookies.admin_session && cookies.authenticated === 'true';
}
},
terminalOnly: {
// Admin CLI - terminal only
enabled: true,
customValidator: (req) => {
return req.headers['x-admin-token'] === process.env.ADMIN_TOKEN;
}
}
}
});
// Admin web interface
server.get('/admin/*', (req, res) => {
res.sendFile('admin.html');
});
// Admin API (browser)
server.get('/admin/api/users', (req, res) => {
res.json({ users: getAllUsers() });
});
// Admin CLI commands (terminal)
server.post('/admin/cli/reset-passwords', (req, res) => {
resetAllPasswords();
res.json({ message: 'All passwords reset' });
});
Security Considerations
1. User Agent Spoofing
// User agents can be spoofed - don't rely solely on them
const server = createServer({
security: {
browserOnly: {
enabled: true,
// Use multiple validation methods
requireHeaders: ['sec-fetch-site', 'sec-fetch-mode'],
customValidator: (req) => {
// Check for browser-specific behavior
return hasBrowserCharacteristics(req);
}
}
}
});
2. Header Manipulation
// Headers can be forged - use multiple signals
const server = createServer({
security: {
terminalOnly: {
enabled: true,
// Combine multiple checks
allowedUserAgents: [/curl\/\d+\.\d+/],
blockBrowserHeaders: ['sec-fetch-mode'],
customValidator: (req) => {
// Additional validation
return req.headers['x-cli-version'] !== undefined;
}
}
}
});
3. False Positives
// Some legitimate clients may be misidentified
const server = createServer({
security: {
browserOnly: {
enabled: true,
allowMissingHeaders: true, // Be more permissive
blockedUserAgents: [
/Bot\/\d+\.\d+/, // Only block obvious bots
/Crawler\/\d+\.\d+/
]
}
}
});
Performance Impact
- Minimal overhead: Simple header and user agent checks
- Fast validation: Regex patterns are pre-compiled
- Early termination: Fails fast on invalid requests
- Memory efficient: No external dependencies
Troubleshooting
Common Issues
-
"Access denied" for legitimate browsers
// Check your validation logic browserOnly: { enabled: true, allowMissingHeaders: true, // More permissive blockedUserAgents: [] // Don't block any user agents } -
CLI tools blocked unexpectedly
// Add your CLI tool to allowed list terminalOnly: { enabled: true, allowedUserAgents: [ /MyCLITool\/\d+\.\d+/, /curl\/\d+\.\d+/ ] } -
Mobile browsers misidentified
// Mobile browsers have different characteristics browserOnly: { enabled: true, requireHeaders: [], // Don't require desktop-specific headers customValidator: (req) => { const ua = req.headers['user-agent'] || ''; return /Mobile|Android|iPhone|iPad/.test(ua) || req.headers['sec-ch-ua-mobile'] === '?1'; } }
Debug Mode
Enable detailed logging:
const server = createServer({
logging: {
level: 'debug',
components: {
security: true
}
},
security: {
browserOnly: {
enabled: true,
debug: true // Enable debug logging
}
}
});
This will log:
[BROWSER_CHECK] User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
[BROWSER_CHECK] Headers present: accept, accept-language, sec-fetch-mode
[BROWSER_CHECK] Validation passed: true
Migration Guide
From Custom Middleware
// Old approach
server.use('/api/web', (req, res, next) => {
const ua = req.headers['user-agent'] || '';
if (!ua.includes('Mozilla')) {
return res.status(403).json({ error: 'Browser required' });
}
next();
});
// New approach
const server = createServer({
security: {
browserOnly: true
}
});
From IP-Based Restrictions
// Old approach (less reliable)
server.use('/admin', (req, res, next) => {
const clientIP = req.ip;
if (!adminIPs.includes(clientIP)) {
return res.status(403).json({ error: 'Access denied' });
}
next();
});
// New approach (more reliable)
const server = createServer({
security: {
browserOnly: {
enabled: true,
customValidator: (req) => {
// Combine IP check with browser validation
const clientIP = req.ip;
return adminIPs.includes(clientIP) && isBrowser(req);
}
}
}
});
Browser Compatibility
Supported Browsers
- ✅ Chrome 77+ (sends
sec-fetch-*headers) - ✅ Firefox 68+ (sends
sec-fetch-*headers) - ✅ Safari 12.1+ (sends
sec-fetch-*headers) - ✅ Edge 79+ (Chromium-based)
Legacy Browser Support
// Fallback for older browsers
const server = createServer({
security: {
browserOnly: {
enabled: true,
requireHeaders: [], // Don't require modern headers
customValidator: (req) => {
const ua = req.headers['user-agent'] || '';
// Fallback to user agent checking
return /Mozilla|Chrome|Safari|Firefox|Edge/.test(ua);
}
}
}
});
Integration Examples
SPA with API Separation
// Frontend routes - browser only
server.get('/app/*', (req, res) => {
res.sendFile('index.html');
});
// API routes - mixed access
server.get('/api/public', (req, res) => {
// Allow both browsers and terminals
res.json({ data: 'public' });
});
// Admin routes - browser only with session validation
server.get('/admin/*', (req, res) => {
// Additional session validation
res.sendFile('admin.html');
});
Microservices Communication
// Service A (web app) - browser only
const webApp = createServer({
security: { browserOnly: true }
});
// Service B (API) - terminal only (server-to-server)
const apiService = createServer({
security: { terminalOnly: true }
});
// Service C (CLI) - terminal only
const cliService = createServer({
security: { terminalOnly: true }
});
Browser/Terminal access control provides fine-grained control over who can access your endpoints. By distinguishing between browser-based users and programmatic clients, you can create more secure and appropriate APIs for different use cases.