Add comprehensive rule documentation for AI-assisted development covering authentication surfaces, outbound-call safety invariants, and other project conventions to guide Claude's understanding of critical system behaviors.
78 lines
3.0 KiB
Python
78 lines
3.0 KiB
Python
"""
|
|
OAuth discovery metadata tests (RFC 9728 / RFC 8414 / RFC 7591).
|
|
|
|
MCP clients that get a 401 from /mcp perform OAuth discovery. These
|
|
endpoints are unauthenticated and served straight from main.app.
|
|
"""
|
|
|
|
import httpx
|
|
import pytest
|
|
|
|
import main
|
|
from config import get_settings
|
|
|
|
|
|
@pytest.fixture
|
|
async def client():
|
|
transport = httpx.ASGITransport(app=main.app)
|
|
async with httpx.AsyncClient(transport=transport, base_url="http://test") as c:
|
|
yield c
|
|
|
|
|
|
class TestProtectedResourceMetadata:
|
|
async def test_resource_advertises_mcp_path(self, client):
|
|
resp = await client.get("/.well-known/oauth-protected-resource")
|
|
assert resp.status_code == 200
|
|
body = resp.json()
|
|
# mcp-remote verifies this matches the URL it connected to.
|
|
assert body["resource"] == "http://test/mcp"
|
|
assert body["authorization_servers"] == ["http://test"]
|
|
|
|
async def test_mcp_suffixed_variant(self, client):
|
|
resp = await client.get("/.well-known/oauth-protected-resource/mcp")
|
|
assert resp.status_code == 200
|
|
assert resp.json()["resource"] == "http://test/mcp"
|
|
|
|
|
|
class TestAuthorizationServerMetadata:
|
|
async def test_advertises_casdoor_when_enabled(self, monkeypatch, client):
|
|
monkeypatch.setattr(get_settings().casdoor, "enabled", True)
|
|
monkeypatch.setattr(get_settings().casdoor, "endpoint", "https://id.example.test")
|
|
resp = await client.get("/.well-known/oauth-authorization-server")
|
|
assert resp.status_code == 200
|
|
body = resp.json()
|
|
assert body["issuer"] == "https://id.example.test"
|
|
assert body["jwks_uri"] == "https://id.example.test/.well-known/jwks"
|
|
assert body["registration_endpoint"] == "http://test/register"
|
|
|
|
async def test_dev_mode_advertises_local(self, monkeypatch, client):
|
|
monkeypatch.setattr(get_settings().casdoor, "enabled", False)
|
|
resp = await client.get("/.well-known/oauth-authorization-server")
|
|
assert resp.status_code == 200
|
|
body = resp.json()
|
|
assert body["issuer"] == "http://test"
|
|
assert body["authorization_endpoint"] == "http://test/auth/login"
|
|
|
|
|
|
class TestDynamicRegistration:
|
|
async def test_registers_client(self, client):
|
|
resp = await client.post(
|
|
"/register",
|
|
json={"redirect_uris": ["http://localhost/cb"], "client_name": "test"},
|
|
)
|
|
assert resp.status_code == 201
|
|
body = resp.json()
|
|
assert "client_id" in body
|
|
assert body["redirect_uris"] == ["http://localhost/cb"]
|
|
|
|
async def test_rejects_missing_redirect_uris(self, client):
|
|
resp = await client.post("/register", json={"client_name": "test"})
|
|
assert resp.status_code == 400
|
|
assert resp.json()["error"] == "invalid_redirect_uri"
|
|
|
|
async def test_rejects_non_json(self, client):
|
|
resp = await client.post(
|
|
"/register", content=b"not json", headers={"content-type": "application/json"}
|
|
)
|
|
assert resp.status_code == 400
|