Jeremiah Lowin commited on
Commit
abb2759
·
1 Parent(s): 91deb88

Add tests; update default issuer

Browse files
src/fastmcp/server/auth/providers/bearer.py CHANGED
@@ -177,7 +177,7 @@ class BearerAuthProvider(OAuthProvider):
177
  raise ValueError("Provide either public_key or jwks_uri, not both")
178
 
179
  super().__init__(
180
- issuer_url=issuer or "http://fastmcp.example.com",
181
  client_registration_options=ClientRegistrationOptions(enabled=False),
182
  revocation_options=RevocationOptions(enabled=False),
183
  required_scopes=required_scopes,
 
177
  raise ValueError("Provide either public_key or jwks_uri, not both")
178
 
179
  super().__init__(
180
+ issuer_url=issuer or "https://fastmcp.example.com",
181
  client_registration_options=ClientRegistrationOptions(enabled=False),
182
  revocation_options=RevocationOptions(enabled=False),
183
  required_scopes=required_scopes,
tests/auth/providers/test_bearer.py CHANGED
@@ -1,4 +1,5 @@
1
  from collections.abc import Generator
 
2
 
3
  import httpx
4
  import pytest
@@ -32,11 +33,17 @@ def bearer_provider(rsa_key_pair: RSAKeyPair) -> BearerAuthProvider:
32
  )
33
 
34
 
35
- def run_mcp_server(public_key: str, host: str, port: int, **kwargs) -> None:
 
 
 
 
 
 
36
  mcp = FastMCP(
37
  auth=BearerAuthProvider(
38
- issuer="https://test.example.com",
39
  public_key=public_key,
 
40
  )
41
  )
42
 
@@ -44,13 +51,15 @@ def run_mcp_server(public_key: str, host: str, port: int, **kwargs) -> None:
44
  def add(a: int, b: int) -> int:
45
  return a + b
46
 
47
- mcp.run(host=host, port=port, **kwargs)
48
 
49
 
50
  @pytest.fixture(scope="module")
51
  def mcp_server_url(rsa_key_pair: RSAKeyPair) -> Generator[str]:
52
  with run_server_in_process(
53
- run_mcp_server, public_key=rsa_key_pair.public_key, transport="streamable-http"
 
 
54
  ) as url:
55
  yield f"{url}/mcp"
56
 
@@ -75,7 +84,8 @@ class TestRSAKeyPair:
75
  def test_create_basic_token(self, rsa_key_pair: RSAKeyPair):
76
  """Test basic token creation."""
77
  token = rsa_key_pair.create_token(
78
- subject="test-user", issuer="https://test.example.com"
 
79
  )
80
 
81
  assert isinstance(token, str)
@@ -359,8 +369,38 @@ class TestFastMCPBearerAuth:
359
  async def test_unauthorized_access(self, mcp_server_url: str):
360
  with pytest.raises(httpx.HTTPStatusError, match="401"):
361
  async with Client(mcp_server_url) as client:
362
- await client.ping()
 
363
 
364
  async def test_authorized_access(self, mcp_server_url: str, bearer_token):
365
  async with Client(mcp_server_url, auth=BearerAuth(bearer_token)) as client:
366
- await client.ping()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  from collections.abc import Generator
2
+ from typing import Any
3
 
4
  import httpx
5
  import pytest
 
33
  )
34
 
35
 
36
+ def run_mcp_server(
37
+ public_key: str,
38
+ host: str,
39
+ port: int,
40
+ auth_kwargs: dict[str, Any] | None = None,
41
+ run_kwargs: dict[str, Any] | None = None,
42
+ ) -> None:
43
  mcp = FastMCP(
44
  auth=BearerAuthProvider(
 
45
  public_key=public_key,
46
+ **auth_kwargs or {},
47
  )
48
  )
49
 
 
51
  def add(a: int, b: int) -> int:
52
  return a + b
53
 
54
+ mcp.run(host=host, port=port, **run_kwargs or {})
55
 
56
 
57
  @pytest.fixture(scope="module")
58
  def mcp_server_url(rsa_key_pair: RSAKeyPair) -> Generator[str]:
59
  with run_server_in_process(
60
+ run_mcp_server,
61
+ public_key=rsa_key_pair.public_key,
62
+ run_kwargs=dict(transport="streamable-http"),
63
  ) as url:
64
  yield f"{url}/mcp"
65
 
 
84
  def test_create_basic_token(self, rsa_key_pair: RSAKeyPair):
85
  """Test basic token creation."""
86
  token = rsa_key_pair.create_token(
87
+ subject="test-user",
88
+ issuer="https://test.example.com",
89
  )
90
 
91
  assert isinstance(token, str)
 
369
  async def test_unauthorized_access(self, mcp_server_url: str):
370
  with pytest.raises(httpx.HTTPStatusError, match="401"):
371
  async with Client(mcp_server_url) as client:
372
+ tools = await client.list_tools() # noqa: F841
373
+ assert "tools" not in locals()
374
 
375
  async def test_authorized_access(self, mcp_server_url: str, bearer_token):
376
  async with Client(mcp_server_url, auth=BearerAuth(bearer_token)) as client:
377
+ tools = await client.list_tools() # noqa: F841
378
+ assert tools
379
+
380
+ async def test_invalid_token_raises_401(self, mcp_server_url: str):
381
+ with pytest.raises(httpx.HTTPStatusError, match="401"):
382
+ async with Client(mcp_server_url, auth=BearerAuth("invalid")) as client:
383
+ tools = await client.list_tools() # noqa: F841
384
+ assert "tools" not in locals()
385
+
386
+ async def test_expired_token(self, mcp_server_url: str, rsa_key_pair: RSAKeyPair):
387
+ token = rsa_key_pair.create_token(
388
+ subject="test-user",
389
+ issuer="https://test.example.com",
390
+ audience="https://api.example.com",
391
+ expires_in_seconds=-3600,
392
+ )
393
+
394
+ with pytest.raises(httpx.HTTPStatusError, match="401"):
395
+ async with Client(mcp_server_url, auth=BearerAuth(token)) as client:
396
+ tools = await client.list_tools() # noqa: F841
397
+ assert "tools" not in locals()
398
+
399
+ async def test_token_with_bad_signature(self, mcp_server_url: str):
400
+ rsa_key_pair = RSAKeyPair.generate()
401
+ token = rsa_key_pair.create_token()
402
+
403
+ with pytest.raises(httpx.HTTPStatusError, match="401"):
404
+ async with Client(mcp_server_url, auth=BearerAuth(token)) as client:
405
+ tools = await client.list_tools() # noqa: F841
406
+ assert "tools" not in locals()