add jwt support
This commit is contained in:
+4
-4
@@ -235,8 +235,8 @@ async def mcp_info():
|
|||||||
"authentication_required": True,
|
"authentication_required": True,
|
||||||
"authentication": {
|
"authentication": {
|
||||||
"type": "oauth2",
|
"type": "oauth2",
|
||||||
"authorization_url": "https://yargimcp.com/sign-in",
|
"authorization_url": "https://yargimcp.com/sign-in?redirect_url=https://api.yargimcp.com/auth/mcp-callback",
|
||||||
"token_url": f"{BASE_URL}/auth/token",
|
"token_url": f"{BASE_URL}/auth/mcp-token",
|
||||||
"scopes": ["read", "search"],
|
"scopes": ["read", "search"],
|
||||||
"provider": "clerk"
|
"provider": "clerk"
|
||||||
},
|
},
|
||||||
@@ -417,7 +417,7 @@ async def validate_clerk_session(request: Request, clerk_token: str = None) -> s
|
|||||||
raise HTTPException(status_code=401, detail=f"Session validation failed: {str(e)}")
|
raise HTTPException(status_code=401, detail=f"Session validation failed: {str(e)}")
|
||||||
|
|
||||||
# MCP OAuth Callback Endpoint
|
# MCP OAuth Callback Endpoint
|
||||||
@app.get("/auth/callback")
|
@app.get("/auth/mcp-callback")
|
||||||
async def mcp_oauth_callback(request: Request, clerk_token: str = Query(None)):
|
async def mcp_oauth_callback(request: Request, clerk_token: str = Query(None)):
|
||||||
"""Handle OAuth callback for MCP token generation"""
|
"""Handle OAuth callback for MCP token generation"""
|
||||||
logger.info(f"MCP OAuth callback - clerk_token provided: {bool(clerk_token)}")
|
logger.info(f"MCP OAuth callback - clerk_token provided: {bool(clerk_token)}")
|
||||||
@@ -511,7 +511,7 @@ async def mcp_oauth_callback(request: Request, clerk_token: str = Query(None)):
|
|||||||
""", status_code=500)
|
""", status_code=500)
|
||||||
|
|
||||||
# MCP Token Endpoint (for OAuth2 compatibility)
|
# MCP Token Endpoint (for OAuth2 compatibility)
|
||||||
@app.post("/auth/token")
|
@app.post("/auth/mcp-token")
|
||||||
async def mcp_token_endpoint(request: Request):
|
async def mcp_token_endpoint(request: Request):
|
||||||
"""OAuth2 token endpoint for MCP clients"""
|
"""OAuth2 token endpoint for MCP clients"""
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -135,19 +135,17 @@ async def authorize_endpoint(
|
|||||||
@router.get("/auth/callback")
|
@router.get("/auth/callback")
|
||||||
async def oauth_callback(
|
async def oauth_callback(
|
||||||
request: Request,
|
request: Request,
|
||||||
state: Optional[str] = Query(None)
|
state: Optional[str] = Query(None),
|
||||||
|
clerk_token: Optional[str] = Query(None)
|
||||||
):
|
):
|
||||||
"""Handle OAuth callback from Clerk - simplified for custom domains"""
|
"""Handle OAuth callback from Clerk - supports both JWT token and cookie auth"""
|
||||||
|
|
||||||
logger.info(f"OAuth callback received - state: {state}")
|
logger.info(f"OAuth callback received - state: {state}")
|
||||||
logger.info(f"Query params: {dict(request.query_params)}")
|
logger.info(f"Query params: {dict(request.query_params)}")
|
||||||
logger.info(f"Cookies: {dict(request.cookies)}")
|
logger.info(f"Cookies: {dict(request.cookies)}")
|
||||||
|
logger.info(f"Clerk JWT token provided: {bool(clerk_token)}")
|
||||||
|
|
||||||
# For Clerk custom domains, we'll assume authentication succeeded
|
# Support both JWT token (for cross-domain) and cookie auth (for subdomain)
|
||||||
# if Clerk redirected the user to our callback URL
|
|
||||||
|
|
||||||
# For custom domains, we'll skip complex session verification
|
|
||||||
# and rely on the fact that Clerk only redirects here after successful auth
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if not state:
|
if not state:
|
||||||
@@ -189,17 +187,71 @@ async def oauth_callback(
|
|||||||
content={"error": "invalid_request", "error_description": "OAuth session expired or not found"}
|
content={"error": "invalid_request", "error_description": "OAuth session expired or not found"}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Check if we have a JWT token (for cross-domain auth)
|
||||||
|
user_authenticated = False
|
||||||
|
auth_method = "none"
|
||||||
|
|
||||||
|
if clerk_token:
|
||||||
|
logger.info("Attempting JWT token validation")
|
||||||
|
try:
|
||||||
|
# Validate JWT token with Clerk
|
||||||
|
from clerk_backend_api import Clerk
|
||||||
|
clerk = Clerk(bearer_auth=os.getenv("CLERK_SECRET_KEY"))
|
||||||
|
|
||||||
|
# Verify the JWT token
|
||||||
|
jwt_claims = clerk.jwt_templates.verify_token(clerk_token)
|
||||||
|
user_id = jwt_claims.get("sub")
|
||||||
|
|
||||||
|
if user_id:
|
||||||
|
logger.info(f"JWT token validation successful - user_id: {user_id}")
|
||||||
|
user_authenticated = True
|
||||||
|
auth_method = "jwt_token"
|
||||||
|
# Store user info in session for token exchange
|
||||||
|
oauth_session["user_id"] = user_id
|
||||||
|
oauth_session["auth_method"] = "jwt_token"
|
||||||
|
else:
|
||||||
|
logger.error("JWT token validation failed - no user_id in claims")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"JWT token validation failed: {str(e)}")
|
||||||
|
# Fall through to cookie validation
|
||||||
|
|
||||||
|
# If no JWT token or validation failed, check cookies
|
||||||
|
if not user_authenticated:
|
||||||
|
logger.info("Checking for Clerk session cookies")
|
||||||
|
# Check for Clerk session cookies (for subdomain auth)
|
||||||
|
clerk_session_cookie = request.cookies.get("__session")
|
||||||
|
if clerk_session_cookie:
|
||||||
|
logger.info("Found Clerk session cookie, assuming authenticated")
|
||||||
|
user_authenticated = True
|
||||||
|
auth_method = "cookie"
|
||||||
|
oauth_session["auth_method"] = "cookie"
|
||||||
|
else:
|
||||||
|
logger.info("No Clerk session cookie found")
|
||||||
|
|
||||||
|
# For custom domains, we'll also trust that Clerk redirected here
|
||||||
|
if not user_authenticated:
|
||||||
|
logger.info("Trusting Clerk redirect for custom domain flow")
|
||||||
|
user_authenticated = True
|
||||||
|
auth_method = "trusted_redirect"
|
||||||
|
oauth_session["auth_method"] = "trusted_redirect"
|
||||||
|
|
||||||
|
logger.info(f"User authenticated: {user_authenticated}, method: {auth_method}")
|
||||||
|
|
||||||
# Generate simple authorization code for custom domain flow
|
# Generate simple authorization code for custom domain flow
|
||||||
auth_code = f"clerk_custom_{session_id}_{int(time.time())}"
|
auth_code = f"clerk_custom_{session_id}_{int(time.time())}"
|
||||||
|
|
||||||
# Store the code mapping for token exchange
|
# Store the code mapping for token exchange
|
||||||
code_data = {
|
code_data = {
|
||||||
"session_id": session_id,
|
"session_id": session_id,
|
||||||
"clerk_authenticated": True,
|
"clerk_authenticated": user_authenticated,
|
||||||
|
"auth_method": auth_method,
|
||||||
"custom_domain_flow": True,
|
"custom_domain_flow": True,
|
||||||
"created_at": time.time(),
|
"created_at": time.time(),
|
||||||
"expires_at": (datetime.utcnow() + timedelta(minutes=5)).timestamp(),
|
"expires_at": (datetime.utcnow() + timedelta(minutes=5)).timestamp(),
|
||||||
}
|
}
|
||||||
|
if "user_id" in oauth_session:
|
||||||
|
code_data["user_id"] = oauth_session["user_id"]
|
||||||
|
|
||||||
oauth_provider.storage.set_session(f"code_{auth_code}", code_data)
|
oauth_provider.storage.set_session(f"code_{auth_code}", code_data)
|
||||||
|
|
||||||
# Build redirect URL back to Claude
|
# Build redirect URL back to Claude
|
||||||
|
|||||||
Reference in New Issue
Block a user