How AuthSec SDK Works
The Complete Authentication Flow
Step 1: Before Authentication - Tools Are Hidden
When your MCP server starts, users only see 5 OAuth tools:
oauth_start- Start authenticationoauth_authenticate- Complete authenticationoauth_status- Check auth statusoauth_logout- Logoutoauth_user_info- Get user info
All your protected business logic tools are completely hidden. Users can't even see they exist.
Step 2: User Starts Authentication
User: Call oauth_start
Server: Here's your session_id and authorization URL
The user opens the URL in their browser and authenticates with your OAuth provider (Google, GitHub, Custom Logon, etc.).
Step 3: Complete Authentication with JWT
After authentication, the user receives a JWT token containing:
{
"email": "john@company.com",
"tenant_id": "acme-corp",
"roles": ["admin", "developer"],
"groups": ["engineering"],
"scopes": ["read", "write"],
"resources": ["projects", "analytics"]
}
They call oauth_authenticate with this token.
Step 4: RBAC Magic - Validating Permissions
Here's where AuthSec SDK does the heavy lifting. For each tool in your server, it:
- Connects to your tenant database (
tenant_acme-corp) - Validates JWT claims against database:
- Does
adminrole exist inrolestable? ✓ - Does
writescope exist inscopestable? ✓ - Does
analyticsresource exist inresourcestable? ✓
- Does
- Checks if user satisfies tool requirements
- Returns list of accessible tools
Step 5: Tools Are Now Visible
After authentication, users see only what they can access:
Available tools:
├── oauth_start
├── oauth_authenticate
├── oauth_status
├── calculator # ← Now visible
├── admin_dashboard # ← Now visible (has admin role)
└── view_analytics # ← Now visible (has permissions)
Step 6: Tool Execution with User Context
When a user calls a protected tool, user information is automatically injected:
@protected_by_AuthSec("admin_dashboard", roles=["admin"])
async def admin_dashboard(arguments: dict) -> list:
# User info automatically available
email = arguments['_user_info']['email'] # "john@company.com"
roles = arguments['_user_info']['roles'] # ["admin", "developer"]
tenant = arguments['_user_info']['tenant_id'] # "acme-corp"
return [{
"type": "text",
"text": f"Welcome to admin dashboard, {email}!"
}]
Architecture Overview
┌─────────────────────────────────┐
│ AI Assistant (Claude, etc.) │
│ MCP Client │
└────────────┬────────────────────┘
│ JSON-RPC 2.0
▼
┌─────────────────────────────────┐
│ Your MCP Server (server.py) │
│ with AuthSec SDK │
│ │
│ OAuth Tools (always visible): │
│ ├── oauth_start │
│ ├── oauth_authenticate │
│ └── oauth_status │
│ │
│ Protected Tools (after auth): │
│ ├── @protected_by_AuthSec │
│ │ ("hello") │
│ ├── @protected_by_AuthSec │
│ │ ("admin_panel", │
│ │ roles=["admin"]) │
│ └── ... │
└────────────┬────────────────────┘
│ HTTPS
▼
┌─────────────────────────────────┐
│ AuthSec SDK Manager Service │
│ (Managed by AuthSec) │
│ │
│ ├── OAuth flow management │
│ ├── JWT validation │
│ ├── RBAC checking │
│ ├── Session management │
│ └── Vault integration │
└────────────┬────────────────────┘
│
▼
┌─────────────────────────────────┐
│ External Services │
│ ├── OAuth Provider │
│ ├── HashiCorp Vault │
│ └── Tenant Database │
└─────────────────────────────────┘
Multi-Tenant Architecture
- Master Database: Tenant mappings
- Tenant Databases: Each tenant has isolated database (
tenant_{tenant_id})- RBAC tables (roles, scopes, resources, permissions)
- Authenticated sessions
- Service configurations
- User data