Skip to content

Commit 89265e7

Browse files
authored
Merge pull request #20 from sacha-development-stuff/codex/fix-too-many-return-statements
Refactor token handler helper flows
2 parents a5c25b1 + edffa10 commit 89265e7

File tree

1 file changed

+148
-144
lines changed

1 file changed

+148
-144
lines changed

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

Lines changed: 148 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,148 @@ def response(self, obj: TokenSuccessResponse | TokenErrorResponse):
113113
},
114114
)
115115

116+
async def _handle_authorization_code(
117+
self, client_info: Any, token_request: AuthorizationCodeRequest
118+
) -> TokenSuccessResponse | TokenErrorResponse:
119+
auth_code = await self.provider.load_authorization_code(client_info, token_request.code)
120+
if auth_code is None or auth_code.client_id != token_request.client_id:
121+
# if code belongs to different client, pretend it doesn't exist
122+
return TokenErrorResponse(
123+
error="invalid_grant",
124+
error_description="authorization code does not exist",
125+
)
126+
127+
# make auth codes expire after a deadline
128+
# see https://datatracker.ietf.org/doc/html/rfc6749#section-10.5
129+
if auth_code.expires_at < time.time():
130+
return TokenErrorResponse(
131+
error="invalid_grant",
132+
error_description="authorization code has expired",
133+
)
134+
135+
# verify redirect_uri doesn't change between /authorize and /tokens
136+
# see https://datatracker.ietf.org/doc/html/rfc6749#section-10.6
137+
if auth_code.redirect_uri_provided_explicitly:
138+
authorize_request_redirect_uri = auth_code.redirect_uri
139+
else:
140+
authorize_request_redirect_uri = None
141+
142+
# Convert both sides to strings for comparison to handle AnyUrl vs string issues
143+
token_redirect_str = str(token_request.redirect_uri) if token_request.redirect_uri is not None else None
144+
auth_redirect_str = (
145+
str(authorize_request_redirect_uri) if authorize_request_redirect_uri is not None else None
146+
)
147+
148+
if token_redirect_str != auth_redirect_str:
149+
return TokenErrorResponse(
150+
error="invalid_request",
151+
error_description=("redirect_uri did not match the one used when creating auth code"),
152+
)
153+
154+
# Verify PKCE code verifier
155+
sha256 = hashlib.sha256(token_request.code_verifier.encode()).digest()
156+
hashed_code_verifier = base64.urlsafe_b64encode(sha256).decode().rstrip("=")
157+
158+
if hashed_code_verifier != auth_code.code_challenge:
159+
# see https://datatracker.ietf.org/doc/html/rfc7636#section-4.6
160+
return TokenErrorResponse(
161+
error="invalid_grant",
162+
error_description="incorrect code_verifier",
163+
)
164+
165+
try:
166+
# Exchange authorization code for tokens
167+
tokens = await self.provider.exchange_authorization_code(client_info, auth_code)
168+
except TokenError as e:
169+
return TokenErrorResponse(
170+
error=e.error,
171+
error_description=e.error_description,
172+
)
173+
174+
return TokenSuccessResponse(root=tokens)
175+
176+
async def _handle_client_credentials(
177+
self, client_info: Any, token_request: ClientCredentialsRequest
178+
) -> TokenSuccessResponse | TokenErrorResponse:
179+
scopes = (
180+
token_request.scope.split(" ")
181+
if token_request.scope
182+
else client_info.scope.split(" ")
183+
if client_info.scope
184+
else []
185+
)
186+
try:
187+
tokens = await self.provider.exchange_client_credentials(client_info, scopes)
188+
except TokenError as e:
189+
return TokenErrorResponse(
190+
error=e.error,
191+
error_description=e.error_description,
192+
)
193+
194+
return TokenSuccessResponse(root=tokens)
195+
196+
async def _handle_token_exchange(
197+
self, client_info: Any, token_request: TokenExchangeRequest
198+
) -> TokenSuccessResponse | TokenErrorResponse:
199+
scopes = token_request.scope.split(" ") if token_request.scope else []
200+
try:
201+
tokens = await self.provider.exchange_token(
202+
client_info,
203+
token_request.subject_token,
204+
token_request.subject_token_type,
205+
token_request.actor_token,
206+
token_request.actor_token_type,
207+
scopes,
208+
token_request.audience,
209+
token_request.resource,
210+
)
211+
except TokenError as e:
212+
return TokenErrorResponse(
213+
error=e.error,
214+
error_description=e.error_description,
215+
)
216+
217+
return TokenSuccessResponse(root=tokens)
218+
219+
async def _handle_refresh_token(
220+
self, client_info: Any, token_request: RefreshTokenRequest
221+
) -> TokenSuccessResponse | TokenErrorResponse:
222+
refresh_token = await self.provider.load_refresh_token(client_info, token_request.refresh_token)
223+
if refresh_token is None or refresh_token.client_id != token_request.client_id:
224+
# if token belongs to a different client, pretend it doesn't exist
225+
return TokenErrorResponse(
226+
error="invalid_grant",
227+
error_description="refresh token does not exist",
228+
)
229+
230+
if refresh_token.expires_at and refresh_token.expires_at < time.time():
231+
# if the refresh token has expired, pretend it doesn't exist
232+
return TokenErrorResponse(
233+
error="invalid_grant",
234+
error_description="refresh token has expired",
235+
)
236+
237+
# Parse scopes if provided
238+
scopes = token_request.scope.split(" ") if token_request.scope else refresh_token.scopes
239+
240+
for scope in scopes:
241+
if scope not in refresh_token.scopes:
242+
return TokenErrorResponse(
243+
error="invalid_scope",
244+
error_description=(f"cannot request scope `{scope}` not provided by refresh token"),
245+
)
246+
247+
try:
248+
# Exchange refresh token for new tokens
249+
tokens = await self.provider.exchange_refresh_token(client_info, refresh_token, scopes)
250+
except TokenError as e:
251+
return TokenErrorResponse(
252+
error=e.error,
253+
error_description=e.error_description,
254+
)
255+
256+
return TokenSuccessResponse(root=tokens)
257+
116258
async def handle(self, request: Request):
117259
try:
118260
form_data = await request.form()
@@ -146,155 +288,17 @@ async def handle(self, request: Request):
146288
)
147289
)
148290

149-
tokens: OAuthToken
150-
151291
match token_request:
152292
case AuthorizationCodeRequest():
153-
auth_code = await self.provider.load_authorization_code(client_info, token_request.code)
154-
if auth_code is None or auth_code.client_id != token_request.client_id:
155-
# if code belongs to different client, pretend it doesn't exist
156-
return self.response(
157-
TokenErrorResponse(
158-
error="invalid_grant",
159-
error_description="authorization code does not exist",
160-
)
161-
)
162-
163-
# make auth codes expire after a deadline
164-
# see https://datatracker.ietf.org/doc/html/rfc6749#section-10.5
165-
if auth_code.expires_at < time.time():
166-
return self.response(
167-
TokenErrorResponse(
168-
error="invalid_grant",
169-
error_description="authorization code has expired",
170-
)
171-
)
172-
173-
# verify redirect_uri doesn't change between /authorize and /tokens
174-
# see https://datatracker.ietf.org/doc/html/rfc6749#section-10.6
175-
if auth_code.redirect_uri_provided_explicitly:
176-
authorize_request_redirect_uri = auth_code.redirect_uri
177-
else:
178-
authorize_request_redirect_uri = None
179-
180-
# Convert both sides to strings for comparison to handle AnyUrl vs string issues
181-
token_redirect_str = str(token_request.redirect_uri) if token_request.redirect_uri is not None else None
182-
auth_redirect_str = (
183-
str(authorize_request_redirect_uri) if authorize_request_redirect_uri is not None else None
184-
)
185-
186-
if token_redirect_str != auth_redirect_str:
187-
return self.response(
188-
TokenErrorResponse(
189-
error="invalid_request",
190-
error_description=("redirect_uri did not match the one used when creating auth code"),
191-
)
192-
)
193-
194-
# Verify PKCE code verifier
195-
sha256 = hashlib.sha256(token_request.code_verifier.encode()).digest()
196-
hashed_code_verifier = base64.urlsafe_b64encode(sha256).decode().rstrip("=")
197-
198-
if hashed_code_verifier != auth_code.code_challenge:
199-
# see https://datatracker.ietf.org/doc/html/rfc7636#section-4.6
200-
return self.response(
201-
TokenErrorResponse(
202-
error="invalid_grant",
203-
error_description="incorrect code_verifier",
204-
)
205-
)
206-
207-
try:
208-
# Exchange authorization code for tokens
209-
tokens = await self.provider.exchange_authorization_code(client_info, auth_code)
210-
except TokenError as e:
211-
return self.response(
212-
TokenErrorResponse(
213-
error=e.error,
214-
error_description=e.error_description,
215-
)
216-
)
293+
result = await self._handle_authorization_code(client_info, token_request)
217294

218295
case ClientCredentialsRequest():
219-
scopes = (
220-
token_request.scope.split(" ")
221-
if token_request.scope
222-
else client_info.scope.split(" ")
223-
if client_info.scope
224-
else []
225-
)
226-
try:
227-
tokens = await self.provider.exchange_client_credentials(client_info, scopes)
228-
except TokenError as e:
229-
return self.response(
230-
TokenErrorResponse(
231-
error=e.error,
232-
error_description=e.error_description,
233-
)
234-
)
296+
result = await self._handle_client_credentials(client_info, token_request)
235297

236298
case TokenExchangeRequest():
237-
scopes = token_request.scope.split(" ") if token_request.scope else []
238-
try:
239-
tokens = await self.provider.exchange_token(
240-
client_info,
241-
token_request.subject_token,
242-
token_request.subject_token_type,
243-
token_request.actor_token,
244-
token_request.actor_token_type,
245-
scopes,
246-
token_request.audience,
247-
token_request.resource,
248-
)
249-
except TokenError as e:
250-
return self.response(
251-
TokenErrorResponse(
252-
error=e.error,
253-
error_description=e.error_description,
254-
)
255-
)
299+
result = await self._handle_token_exchange(client_info, token_request)
256300

257301
case RefreshTokenRequest():
258-
refresh_token = await self.provider.load_refresh_token(client_info, token_request.refresh_token)
259-
if refresh_token is None or refresh_token.client_id != token_request.client_id:
260-
# if token belongs to a different client, pretend it doesn't exist
261-
return self.response(
262-
TokenErrorResponse(
263-
error="invalid_grant",
264-
error_description="refresh token does not exist",
265-
)
266-
)
267-
268-
if refresh_token.expires_at and refresh_token.expires_at < time.time():
269-
# if the refresh token has expired, pretend it doesn't exist
270-
return self.response(
271-
TokenErrorResponse(
272-
error="invalid_grant",
273-
error_description="refresh token has expired",
274-
)
275-
)
276-
277-
# Parse scopes if provided
278-
scopes = token_request.scope.split(" ") if token_request.scope else refresh_token.scopes
279-
280-
for scope in scopes:
281-
if scope not in refresh_token.scopes:
282-
return self.response(
283-
TokenErrorResponse(
284-
error="invalid_scope",
285-
error_description=(f"cannot request scope `{scope}` not provided by refresh token"),
286-
)
287-
)
288-
289-
try:
290-
# Exchange refresh token for new tokens
291-
tokens = await self.provider.exchange_refresh_token(client_info, refresh_token, scopes)
292-
except TokenError as e:
293-
return self.response(
294-
TokenErrorResponse(
295-
error=e.error,
296-
error_description=e.error_description,
297-
)
298-
)
299-
300-
return self.response(TokenSuccessResponse(root=tokens))
302+
result = await self._handle_refresh_token(client_info, token_request)
303+
304+
return self.response(result)

0 commit comments

Comments
 (0)