Skip to content

Commit 482149e

Browse files
committed
Fix redirect_uri handling
1 parent c6f991b commit 482149e

File tree

4 files changed

+9
-4
lines changed

4 files changed

+9
-4
lines changed

src/mcp/server/auth/handlers/authorize.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ async def error_response(
197197
scopes=scopes,
198198
code_challenge=auth_request.code_challenge,
199199
redirect_uri=redirect_uri,
200+
redirect_uri_provided_explicitly=auth_request.redirect_uri is not None,
200201
)
201202

202203
try:

src/mcp/server/auth/handlers/token.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class AuthorizationCodeRequest(BaseModel):
2525
grant_type: Literal["authorization_code"]
2626
code: str = Field(..., description="The authorization code")
2727
redirect_uri: AnyHttpUrl | None = Field(
28-
..., description="Must be the same as redirect URI provided in /authorize"
28+
None, description="Must be the same as redirect URI provided in /authorize"
2929
)
3030
client_id: str
3131
# we use the client_secret param, per https://datatracker.ietf.org/doc/html/rfc6749#section-2.3.1
@@ -158,7 +158,8 @@ async def handle(self, request: Request):
158158

159159
# verify redirect_uri doesn't change between /authorize and /tokens
160160
# see https://datatracker.ietf.org/doc/html/rfc6749#section-10.6
161-
if token_request.redirect_uri != auth_code.redirect_uri:
161+
authorize_request_redirect_uri = auth_code.redirect_uri if auth_code.redirect_uri_provided_explicitly else None
162+
if token_request.redirect_uri != authorize_request_redirect_uri:
162163
return self.response(
163164
TokenErrorResponse(
164165
error="invalid_request",

src/mcp/server/auth/provider.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@
1111

1212

1313
class AuthorizationParams(BaseModel):
14-
state: str | None = None
15-
scopes: list[str] | None = None
14+
state: str | None
15+
scopes: list[str] | None
1616
code_challenge: str
1717
redirect_uri: AnyHttpUrl
18+
redirect_uri_provided_explicitly: bool
1819

1920

2021
class AuthorizationCode(BaseModel):
@@ -24,6 +25,7 @@ class AuthorizationCode(BaseModel):
2425
client_id: str
2526
code_challenge: str
2627
redirect_uri: AnyHttpUrl
28+
redirect_uri_provided_explicitly: bool
2729

2830

2931
class RefreshToken(BaseModel):

tests/server/fastmcp/auth/test_auth_integration.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ async def authorize(
6464
client_id=client.client_id,
6565
code_challenge=params.code_challenge,
6666
redirect_uri=params.redirect_uri,
67+
redirect_uri_provided_explicitly=params.redirect_uri_provided_explicitly,
6768
expires_at=time.time() + 300,
6869
scopes=params.scopes or ["read", "write"],
6970
)

0 commit comments

Comments
 (0)