85 lines
2.3 KiB
PHP
85 lines
2.3 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Core\Mod\Mcp\Middleware;
|
|
|
|
use Core\Mod\Api\Models\ApiKey;
|
|
use Closure;
|
|
use Illuminate\Http\Request;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
|
|
/**
|
|
* API Key authentication for MCP HTTP API.
|
|
*
|
|
* Supports:
|
|
* - Authorization: Bearer hk_xxx_yyy
|
|
* - X-API-Key: hk_xxx_yyy
|
|
*
|
|
* Also enforces per-server access scopes on tool execution.
|
|
*/
|
|
class McpApiKeyAuth
|
|
{
|
|
public function handle(Request $request, Closure $next): Response
|
|
{
|
|
$key = $this->extractKey($request);
|
|
|
|
if (! $key) {
|
|
return response()->json([
|
|
'error' => 'Missing API key',
|
|
'hint' => 'Provide via Authorization: Bearer <key> or X-API-Key header',
|
|
], 401);
|
|
}
|
|
|
|
$apiKey = ApiKey::findByPlainKey($key);
|
|
|
|
if (! $apiKey) {
|
|
return response()->json([
|
|
'error' => 'Invalid API key',
|
|
], 401);
|
|
}
|
|
|
|
if ($apiKey->isExpired()) {
|
|
return response()->json([
|
|
'error' => 'API key has expired',
|
|
], 401);
|
|
}
|
|
|
|
// Check server-level access for tool calls
|
|
if ($request->is('*/tools/call') && $request->isMethod('POST')) {
|
|
$serverId = $request->input('server');
|
|
if ($serverId && ! $apiKey->hasServerAccess($serverId)) {
|
|
return response()->json([
|
|
'error' => 'Access denied to server: '.$serverId,
|
|
'allowed_servers' => $apiKey->getAllowedServers(),
|
|
], 403);
|
|
}
|
|
}
|
|
|
|
// Record usage
|
|
$apiKey->recordUsage();
|
|
|
|
// Attach to request for controller access
|
|
$request->attributes->set('api_key', $apiKey);
|
|
$request->attributes->set('workspace', $apiKey->workspace);
|
|
|
|
return $next($request);
|
|
}
|
|
|
|
protected function extractKey(Request $request): ?string
|
|
{
|
|
// Try Authorization: Bearer
|
|
$authHeader = $request->header('Authorization');
|
|
if ($authHeader && str_starts_with($authHeader, 'Bearer ')) {
|
|
return substr($authHeader, 7);
|
|
}
|
|
|
|
// Try X-API-Key
|
|
$apiKeyHeader = $request->header('X-API-Key');
|
|
if ($apiKeyHeader) {
|
|
return $apiKeyHeader;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|