# Backend trust & verification Ensure secure communication between the Frontegg MCP Gateway and your backend API by verifying request signatures. ## Overview When the MCP Gateway routes requests to your backend API, it includes a cryptographic signature in the request headers. This signature allows your backend to verify that requests are genuinely coming from Frontegg and haven't been tampered with in transit. ## How it works The MCP Gateway signs each outgoing request using the **Key secret** you configure in the **[Portal] → [Settings] → [Basic Configuration]**. This signature is added to the request header as `frontegg-mcp-signature`. ### Signature Components The signature is generated using the following request components: - **HTTP Method** (GET, POST, PUT, etc.) - **Request URL** (full URL including query parameters) - **Request Headers** - **Request Body** (if present) ### Signature algorithm - **Algorithm**: SHA-256 HMAC - **Validity Window**: 300 seconds (5 minutes) - **Secret**: The Key secret configured in your MCP Gateway settings ## Configuring your key secret 1. Navigate to the **Configuration** tab in your agent application 2. Locate the **MCP Gateway** section 3. Copy or generate your **Key secret** 4. Store this secret securely in your backend application > **Important**: Keep your Key secret secure and never expose it in client-side code or public repositories. ## Verifying signatures on your backend To verify incoming requests from the MCP Gateway, your backend must: 1. **Extract the signature** from the `frontegg-mcp-signature` header 2. **Reconstruct the signature** using the same components: - Request method - Request URL - Request headers - Request body (if present) 3. **Generate a signature** using your Key secret and SHA-256 HMAC 4. **Compare signatures** to ensure they match ### Validity window Signatures include a timestamp and are valid for **5 minutes** from creation. This prevents replay attacks where old requests might be reused maliciously. Your backend should: - Check the timestamp in the signature - Reject requests with signatures older than 5 minutes ## Code examples ### TypeScript ```typescript import * as crypto from 'crypto'; interface SignatureComponents { signature: string; timestamp: string; } function parseSignatureHeader(header: string): SignatureComponents { const parts = header.split(','); const signature = parts.find(p => p.startsWith('signature='))?.split('=')[1] || ''; const timestamp = parts.find(p => p.startsWith('t='))?.split('=')[1] || ''; return { signature, timestamp }; } function verifySignature( method: string, url: string, headers: Record, body: string | null, signatureHeader: string, secret: string ): boolean { // Parse the signature header const { signature, timestamp } = parseSignatureHeader(signatureHeader); // Check timestamp validity (within 5 minutes) const currentTime = Math.floor(Date.now() / 1000); const requestTime = parseInt(timestamp, 10); if (currentTime - requestTime > 300) { console.error('Signature expired'); return false; } // Construct the string to sign const headersString = JSON.stringify(headers); const bodyString = body || ''; const stringToSign = `${method}:${url}:${headersString}:${bodyString}:${timestamp}`; // Generate HMAC signature const hmac = crypto.createHmac('sha256', secret); hmac.update(stringToSign); const computedSignature = hmac.digest('hex'); // Compare signatures return signature === computedSignature; } // Example usage in an Express middleware function fronteggSignatureMiddleware(secret: string) { return (req: any, res: any, next: any) => { const signatureHeader = req.headers['frontegg-mcp-signature']; if (!signatureHeader) { return res.status(401).json({ error: 'Missing signature header' }); } const body = req.body ? JSON.stringify(req.body) : null; const isValid = verifySignature( req.method, req.originalUrl, req.headers, body, signatureHeader, secret ); if (!isValid) { return res.status(401).json({ error: 'Invalid signature' }); } next(); }; } ``` ### Python ```python import hmac import hashlib import time import json from typing import Dict, Optional, Tuple def parse_signature_header(header: str) -> Tuple[str, str]: """Parse the frontegg-mcp-signature header.""" parts = header.split(',') signature = '' timestamp = '' for part in parts: if part.startswith('signature='): signature = part.split('=', 1)[1] elif part.startswith('t='): timestamp = part.split('=', 1)[1] return signature, timestamp def verify_signature( method: str, url: str, headers: Dict[str, str], body: Optional[str], signature_header: str, secret: str ) -> bool: """Verify the Frontegg MCP Gateway signature.""" # Parse the signature header signature, timestamp = parse_signature_header(signature_header) # Check timestamp validity (within 5 minutes) current_time = int(time.time()) request_time = int(timestamp) if current_time - request_time > 300: print('Signature expired') return False # Construct the string to sign headers_string = json.dumps(headers, separators=(',', ':')) body_string = body or '' string_to_sign = f"{method}:{url}:{headers_string}:{body_string}:{timestamp}" # Generate HMAC signature computed_signature = hmac.new( secret.encode('utf-8'), string_to_sign.encode('utf-8'), hashlib.sha256 ).hexdigest() # Compare signatures (constant-time comparison) return hmac.compare_digest(signature, computed_signature) # Example usage in a Flask application from flask import Flask, request, jsonify app = Flask(__name__) SECRET = 'your-key-secret-from-frontegg' def require_valid_signature(f): """Decorator to verify Frontegg signature.""" def decorated_function(*args, **kwargs): signature_header = request.headers.get('frontegg-mcp-signature') if not signature_header: return jsonify({'error': 'Missing signature header'}), 401 body = request.get_data(as_text=True) if request.data else None is_valid = verify_signature( method=request.method, url=request.url, headers=dict(request.headers), body=body, signature_header=signature_header, secret=SECRET ) if not is_valid: return jsonify({'error': 'Invalid signature'}), 401 return f(*args, **kwargs) decorated_function.__name__ = f.__name__ return decorated_function @app.route('/api/endpoint', methods=['POST']) @require_valid_signature def protected_endpoint(): return jsonify({'message': 'Request verified successfully'}) ``` > **Note**: These examples show the general approach to signature verification. You may need to adjust the string-to-sign construction based on the exact format used by the Frontegg MCP Gateway. Contact Frontegg support for the precise signature format specification. ## Security best practices - **Rotate secrets regularly**: Update your Key secret periodically and whenever a team member with access leaves - **Use HTTPS**: Always use HTTPS for your API endpoints to prevent man-in-the-middle attacks - **Log verification failures**: Monitor failed signature verifications as they may indicate security issues - **Reject invalid signatures**: Never process requests with invalid or missing signatures - **Secure secret storage**: Store your Key secret using secure secret management solutions (e.g., AWS Secrets Manager, HashiCorp Vault) ## Troubleshooting ### Signature verification fails If signature verification fails, check: 1. **Correct secret**: Ensure your backend is using the exact Key secret from the Configuration page 2. **Clock synchronization**: Verify your server's clock is synchronized (use NTP) 3. **Timestamp expiration**: Check if the request is being processed within the 5-minute validity window 4. **Request modifications**: Ensure no proxies or middleware are modifying the request before verification ### Invalid timestamp errors If you're receiving timestamp-related errors: - Check your server's system time - Ensure your timezone settings are correct - Consider network latency between the gateway and your backend