1212import time
1313from collections .abc import AsyncGenerator , Awaitable , Callable
1414from dataclasses import dataclass , field
15- from typing import Any , Protocol
15+ from typing import Any , NewType , Protocol
1616from urllib .parse import quote , urlencode , urljoin , urlparse
1717
1818import anyio
5353
5454logger = logging .getLogger (__name__ )
5555
56+ AuthorizationState = NewType ("AuthorizationState" , str )
57+
5658
5759class PKCEParameters (BaseModel ):
5860 """PKCE (Proof Key for Code Exchange) parameters."""
@@ -305,14 +307,10 @@ async def _perform_authorization(self) -> httpx.Request:
305307 token_request = await self ._exchange_token_authorization_code (auth_code , code_verifier )
306308 return token_request
307309
308- async def _perform_authorization_code_grant (self ) -> tuple [str , str ]:
309- """Perform the authorization redirect and get auth code ."""
310+ async def _build_authorization_url (self ) -> tuple [str , AuthorizationState , PKCEParameters ]:
311+ """Build authorization URL and state ."""
310312 if self .context .client_metadata .redirect_uris is None :
311313 raise OAuthFlowError ("No redirect URIs provided for authorization code grant" ) # pragma: no cover
312- if not self .context .redirect_handler :
313- raise OAuthFlowError ("No redirect handler provided for authorization code grant" ) # pragma: no cover
314- if not self .context .callback_handler :
315- raise OAuthFlowError ("No callback handler provided for authorization code grant" ) # pragma: no cover
316314
317315 if self .context .oauth_metadata and self .context .oauth_metadata .authorization_endpoint :
318316 auth_endpoint = str (self .context .oauth_metadata .authorization_endpoint ) # pragma: no cover
@@ -325,7 +323,7 @@ async def _perform_authorization_code_grant(self) -> tuple[str, str]:
325323
326324 # Generate PKCE parameters
327325 pkce_params = PKCEParameters .generate ()
328- state = secrets .token_urlsafe (32 )
326+ state = AuthorizationState ( secrets .token_urlsafe (32 ) )
329327
330328 auth_params = {
331329 "response_type" : "code" ,
@@ -344,6 +342,17 @@ async def _perform_authorization_code_grant(self) -> tuple[str, str]:
344342 auth_params ["scope" ] = self .context .client_metadata .scope
345343
346344 authorization_url = f"{ auth_endpoint } ?{ urlencode (auth_params )} "
345+
346+ return authorization_url , state , pkce_params
347+
348+ async def _perform_authorization_code_grant (self ) -> tuple [str , str ]:
349+ """Perform the authorization redirect and get auth code."""
350+ if not self .context .redirect_handler :
351+ raise OAuthFlowError ("No redirect handler provided for authorization code grant" ) # pragma: no cover
352+ if not self .context .callback_handler :
353+ raise OAuthFlowError ("No callback handler provided for authorization code grant" ) # pragma: no cover
354+
355+ authorization_url , state , pkce_params = await self ._build_authorization_url ()
347356 await self .context .redirect_handler (authorization_url )
348357
349358 # Wait for callback
0 commit comments