9.2 KiB
User Management System Setup Guide
This guide explains how to integrate the user management system into your MCP Gateway, enabling per-user API key generation and MCP access control.
Overview
The user management system provides:
- User Creation & Management: Create and manage users, enable/disable accounts
- API Key Generation: Generate unique API keys with optional TTL (time-to-live)
- MCP Access Control: Allow/block specific MCPs for each user (e.g., some users access only ERPNext, others access Wave + TrueNAS)
- Persistent Storage: User data stored in JSON file, survives container restarts
- Web Dashboard: Intuitive UI for managing users and API keys
Files Created
- user_management.py - Core user/API key management logic
- user_routes.py - REST API endpoints for user management
- user_dashboard_ui.py - Web dashboard with Vue.js UI
- gateway_proxy_user_integration.py - Integration instructions
Installation Steps
Step 1: Copy Files
The files are already created in gateway-proxy/:
user_management.pyuser_routes.pyuser_dashboard_ui.py
Step 2: Update gateway_proxy.py
Add the import at line 33 (after existing imports):
from user_management import user_manager
from user_routes import (
create_user, list_users, get_user, delete_user, toggle_user,
generate_api_key, revoke_api_key, set_mcp_access
)
from user_dashboard_ui import user_management_dashboard
Step 3: Update validate_bearer_token() Function
Replace the validate_bearer_token() function (around line 254) with:
def validate_bearer_token(request: Request) -> dict | None:
auth_header = request.headers.get("Authorization", "")
if not auth_header.startswith("Bearer "):
return None
token = auth_header[7:]
# Check static API key first
if STATIC_API_KEY and token == STATIC_API_KEY:
return {"client_id": "static", "scope": "mcp:tools", "user": "static"}
# Check user API key
user_info = user_manager.validate_api_key(token)
if user_info:
return {
"client_id": "api-key",
"scope": "mcp:tools",
"user": user_info['username'],
"user_id": user_info['user_id'],
"mcp_allowed": user_info['mcp_allowed'],
"mcp_blocked": user_info['mcp_blocked']
}
# Check OAuth token
token_hash = _hash(token)
info = ACCESS_TOKENS.get(token_hash)
if not info:
return None
if info["expires_at"] < time.time():
del ACCESS_TOKENS[token_hash]
return None
return info
Step 4: Add MCP Access Control to handle_mcp()
In the handle_mcp() function, after retrieving token_info, add this check:
# Check MCP access control
if token_info and 'user' in token_info and token_info.get('client_id') == 'api-key':
mcp_allowed = token_info.get('mcp_allowed', [])
mcp_blocked = token_info.get('mcp_blocked', [])
# Check if user can access any backend (basic check)
if not mcp_allowed and not mcp_blocked:
# User has no restrictions, allow all
pass
elif mcp_blocked and len(mcp_blocked) == len(BACKENDS):
return JSONResponse(
{'error': 'access_denied', 'message': 'All MCPs are blocked for your user'},
status_code=403
)
Step 5: Update Routes List
Add these routes to the routes list before creating the app (around line 1012):
routes = [
# ... existing well-known and OAuth routes ...
# MCP endpoint
Route("/mcp", handle_mcp, methods=["GET", "HEAD", "POST", "DELETE"]),
# Monitoring
Route("/health", health, methods=["GET"]),
Route("/status", status, methods=["GET"]),
# Dashboard
Route("/dashboard", dashboard, methods=["GET"]),
Route("/dashboard/status", dashboard_status, methods=["GET"]),
# User Management - Enhanced Dashboard
Route("/admin", user_management_dashboard, methods=["GET"]),
# User Management API
Route("/users", create_user, methods=["POST"]),
Route("/users", list_users, methods=["GET"]),
Route("/users/{username}", get_user, methods=["GET"]),
Route("/users/{username}", delete_user, methods=["DELETE"]),
Route("/users/{username}/enable", toggle_user, methods=["PATCH"]),
Route("/users/{username}/keys", generate_api_key, methods=["POST"]),
Route("/users/{username}/mcp-access", set_mcp_access, methods=["PUT"]),
Route("/keys/revoke", revoke_api_key, methods=["POST"]),
]
Step 6: Update docker-compose.yml
Ensure the gateway service has a data volume for persistent storage:
services:
mcp-gateway:
image: mcp-gateway:latest
build:
context: ./gateway-proxy
dockerfile: Dockerfile
container_name: mcp-gateway
ports:
- "4444:4444"
environment:
# ... existing environment variables ...
USERS_DB_PATH: /data/users.json
volumes:
- ./data:/data
- ./gateway-proxy:/app
depends_on:
# ... existing dependencies ...
Step 7: Create Data Directory
mkdir -p /sessions/vigilant-elegant-ramanujan/mnt/MCP\ Servers/mcp-gateway/data
Step 8: Restart Gateway
docker-compose down
docker-compose up -d mcp-gateway
Usage
Access the Dashboard
Open your browser to:
http://10.0.0.25:4444/admin
Create a User via API
curl -X POST http://10.0.0.25:4444/users \
-H "Content-Type: application/json" \
-d '{
"username": "alice",
"email": "alice@example.com",
"description": "Engineering team"
}'
Generate an API Key
curl -X POST http://10.0.0.25:4444/users/alice/keys \
-H "Content-Type: application/json" \
-d '{
"key_name": "production-key",
"ttl_days": 90
}'
Response:
{
"status": "created",
"username": "alice",
"api_key": "mcpgw_...",
"note": "Save this key immediately — it will not be shown again"
}
Configure MCP Access
curl -X PUT http://10.0.0.25:4444/users/alice/mcp-access \
-H "Content-Type: application/json" \
-d '{
"allowed_mcps": ["erpnext", "wave"],
"blocked_mcps": []
}'
This allows alice to access only ERPNext and Wave Finance MCPs.
Use the API Key
curl -X POST http://10.0.0.25:4444/mcp \
-H "Authorization: Bearer mcpgw_..." \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {},
"clientInfo": {"name": "test-client", "version": "1.0"}
}
}'
Access Control Examples
Example 1: Limited Access User
Create a user with access to only ERPNext:
# Create user
curl -X POST http://10.0.0.25:4444/users \
-H "Content-Type: application/json" \
-d '{"username": "erp-user", "description": "ERP access only"}'
# Generate key
curl -X POST http://10.0.0.25:4444/users/erp-user/keys \
-H "Content-Type: application/json" \
-d '{"key_name": "erp-only"}'
# Restrict to ERPNext
curl -X PUT http://10.0.0.25:4444/users/erp-user/mcp-access \
-H "Content-Type: application/json" \
-d '{"allowed_mcps": ["erpnext"]}'
Example 2: Block Specific MCPs
Create a user with access to everything except Home Assistant:
curl -X PUT http://10.0.0.25:4444/users/alice/mcp-access \
-H "Content-Type: application/json" \
-d '{"blocked_mcps": ["homeassistant"]}'
Example 3: Revoke an API Key
curl -X POST http://10.0.0.25:4444/keys/revoke \
-H "Content-Type: application/json" \
-d '{"api_key": "mcpgw_..."}'
Database Schema
User data is stored in /data/users.json with the following structure:
{
"users": {
"alice": {
"user_id": "abc123...",
"email": "alice@example.com",
"description": "Engineering team",
"created_at": "2026-03-31T12:00:00.000000",
"enabled": true,
"api_keys": ["hash1", "hash2"],
"mcp_allowed": ["erpnext", "wave"],
"mcp_blocked": [],
"metadata": {}
}
},
"api_keys": {
"hash1": {
"user_id": "abc123...",
"username": "alice",
"key_name": "prod-key",
"created_at": "2026-03-31T12:00:00.000000",
"expires_at": null,
"revoked": false
}
}
}
Security Notes
- API Keys are Hashed: Only SHA256 hashes are stored, never the actual key
- One-Time Display: Keys are shown only once during generation
- TTL Support: Keys can expire after N days
- Revocation: Keys can be revoked at any time
- Per-User Access Control: Each user has independent MCP restrictions
Troubleshooting
Users/Keys Not Persisting After Restart
Check that:
/datadirectory exists and is mounted in docker-compose.yml/datadirectory has write permissionsUSERS_DB_PATH=/data/users.jsonis set in environment
API Key Not Working
- Verify key hasn't been revoked: Check in dashboard
- Verify key hasn't expired: Check
expires_attimestamp - Verify user is enabled: Check
enabledfield in dashboard - Check MCP access: Verify the user has access to the MCP they're trying to use
Next Steps
- Configure additional users for different teams/roles
- Set up automated key rotation policies
- Monitor API key usage via the dashboard
- Integrate with your CI/CD for automated deployments