Spaces:
Paused
Paused
Selcan Yukcu commited on
Commit ·
92d7038
1
Parent(s): 7f82b3d
feat(auth): implement Keycloak authentication and configuration
Browse files- main.py +7 -10
- utils/auth.py +34 -0
- utils/config.py +16 -1
main.py
CHANGED
|
@@ -3,13 +3,13 @@ from contextlib import asynccontextmanager
|
|
| 3 |
import httpx
|
| 4 |
from fastmcp import FastMCP, Context
|
| 5 |
from loguru import logger
|
| 6 |
-
from dataclasses import dataclass
|
| 7 |
import uuid
|
| 8 |
import os
|
| 9 |
import sys
|
| 10 |
|
| 11 |
from utils.config import API_CONFIG
|
| 12 |
from utils.model import MultiModuleRequest, SingleModuleRequest, SearchSpaceRoutingRequest
|
|
|
|
| 13 |
|
| 14 |
from fastmcp.server.dependencies import get_http_request
|
| 15 |
from starlette.requests import Request
|
|
@@ -19,18 +19,15 @@ from dotenv import load_dotenv
|
|
| 19 |
|
| 20 |
load_dotenv()
|
| 21 |
|
| 22 |
-
|
| 23 |
-
@dataclass
|
| 24 |
-
class DSContext:
|
| 25 |
-
"""Typed context for DS MCP server"""
|
| 26 |
-
|
| 27 |
-
client: httpx.AsyncClient
|
| 28 |
-
|
| 29 |
-
|
| 30 |
@asynccontextmanager
|
| 31 |
async def ds_lifespan(server: FastMCP) -> AsyncIterator[DSContext]:
|
| 32 |
client = httpx.AsyncClient(timeout=API_CONFIG["timeout"])
|
| 33 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
|
| 35 |
try:
|
| 36 |
yield ctx
|
|
|
|
| 3 |
import httpx
|
| 4 |
from fastmcp import FastMCP, Context
|
| 5 |
from loguru import logger
|
|
|
|
| 6 |
import uuid
|
| 7 |
import os
|
| 8 |
import sys
|
| 9 |
|
| 10 |
from utils.config import API_CONFIG
|
| 11 |
from utils.model import MultiModuleRequest, SingleModuleRequest, SearchSpaceRoutingRequest
|
| 12 |
+
from utils.auth import DSContext, authenticate
|
| 13 |
|
| 14 |
from fastmcp.server.dependencies import get_http_request
|
| 15 |
from starlette.requests import Request
|
|
|
|
| 19 |
|
| 20 |
load_dotenv()
|
| 21 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
@asynccontextmanager
|
| 23 |
async def ds_lifespan(server: FastMCP) -> AsyncIterator[DSContext]:
|
| 24 |
client = httpx.AsyncClient(timeout=API_CONFIG["timeout"])
|
| 25 |
+
token = await authenticate()
|
| 26 |
+
|
| 27 |
+
if token:
|
| 28 |
+
client.headers.update({"Authorization": f"Bearer {token}"})
|
| 29 |
+
|
| 30 |
+
ctx = DSContext(client=client, access_token=token)
|
| 31 |
|
| 32 |
try:
|
| 33 |
yield ctx
|
utils/auth.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Optional
|
| 2 |
+
import httpx
|
| 3 |
+
from dataclasses import dataclass
|
| 4 |
+
|
| 5 |
+
from utils.config import KEYCLOAK_CONFIG, TOKEN_ENDPOINT
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
@dataclass
|
| 9 |
+
class DSContext:
|
| 10 |
+
"""Typed context for DS MCP server"""
|
| 11 |
+
|
| 12 |
+
client: httpx.AsyncClient
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
async def authenticate() -> Optional[str]:
|
| 17 |
+
"""Authenticate with Keycloak and return access token"""
|
| 18 |
+
data = {
|
| 19 |
+
"grant_type": KEYCLOAK_CONFIG["grant_type"],
|
| 20 |
+
"client_id": KEYCLOAK_CONFIG["client_id"],
|
| 21 |
+
"username": KEYCLOAK_CONFIG["username"],
|
| 22 |
+
"password": KEYCLOAK_CONFIG["password"],
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
if KEYCLOAK_CONFIG["client_secret"]:
|
| 26 |
+
data["client_secret"] = KEYCLOAK_CONFIG["client_secret"]
|
| 27 |
+
|
| 28 |
+
async with httpx.AsyncClient() as client:
|
| 29 |
+
response = await client.post(TOKEN_ENDPOINT, data=data)
|
| 30 |
+
if response.status_code == 200:
|
| 31 |
+
return response.json().get("access_token")
|
| 32 |
+
else:
|
| 33 |
+
print(f"Authentication failed: {response.status_code} - {response.text}")
|
| 34 |
+
return None
|
utils/config.py
CHANGED
|
@@ -11,4 +11,19 @@ API_CONFIG = {
|
|
| 11 |
"api_searchspace_endpoint": str(os.getenv("DS_API_SEARCHSPACE_ENDPOINT", "/mcp_search_space_routing")),
|
| 12 |
"hf_access_token": os.getenv("HF_ACCESS_TOKEN", None),
|
| 13 |
"timeout": int(os.getenv("TIMEOUT", 120)),
|
| 14 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
"api_searchspace_endpoint": str(os.getenv("DS_API_SEARCHSPACE_ENDPOINT", "/mcp_search_space_routing")),
|
| 12 |
"hf_access_token": os.getenv("HF_ACCESS_TOKEN", None),
|
| 13 |
"timeout": int(os.getenv("TIMEOUT", 120)),
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
# Keycloak Configuration
|
| 18 |
+
KEYCLOAK_CONFIG = {
|
| 19 |
+
"base_url": os.getenv("KEYCLOAK_BASE_URL", "https://training.test.orbitant.dev"),
|
| 20 |
+
"realm": os.getenv("KEYCLOAK_REALM", "orbitant-realm"),
|
| 21 |
+
"client_id": os.getenv("KEYCLOAK_CLIENT_ID", "orbitant-backend-client"),
|
| 22 |
+
"client_secret": os.getenv("KEYCLOAK_CLIENT_SECRET", " "),
|
| 23 |
+
"grant_type": os.getenv("KEYCLOAK_GRANT_TYPE", "password"),
|
| 24 |
+
"username": os.getenv("KEYCLOAK_USERNAME", "admin"),
|
| 25 |
+
"password": os.getenv("KEYCLOAK_PASSWORD", "admin"),
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
# Token Endpoint
|
| 29 |
+
TOKEN_ENDPOINT = f"{KEYCLOAK_CONFIG['base_url']}/realms/{KEYCLOAK_CONFIG['realm']}/protocol/openid-connect/token"
|