1717----
1818"""
1919
20- from dataclasses import asdict
20+ from dataclasses import asdict , dataclass
2121from http import HTTPStatus
2222from typing import Any , Dict , Generic , List , Optional , Tuple , Type , Union , get_args
2323
24+ from .models import Client
2425from .requests import Request
2526from .types import UserType
2627from .storage import BaseStorage
7172)
7273
7374
75+ @dataclass
76+ class AuthorizationState (Generic [UserType ]):
77+ """AuthorizationServer state object used in Authorization Code process."""
78+
79+ request : Request [UserType ]
80+ """OAuth2.0 Authorization Code Request Object"""
81+
82+ response_type_list : List [ResponseType ]
83+ """Supported ResponseTypes Collected During Initial Request Validation"""
84+
85+ grants : List [Tuple [ResponseTypeAuthorizationCode [UserType ], Client ]]
86+ """Collection of Supported GrantType Handlers and The Parsed Clients"""
87+
88+
7489class AuthorizationServer (Generic [UserType ]):
7590 """Interface for initializing an OAuth 2.0 server."""
7691
@@ -341,13 +356,14 @@ async def token(request: fastapi.Request) -> fastapi.Response:
341356 InvalidRedirectURIError ,
342357 )
343358 )
344- async def create_authorization_response (
359+ async def validate_authorization_request (
345360 self , request : Request [UserType ]
346- ) -> Response :
361+ ) -> Union [ Response , AuthorizationState ] :
347362 """
348363 Endpoint to interact with the resource owner and obtain an
349- authorization grant.
350- Validate authorization request and create authorization response.
364+ authoriation grant.
365+ Validate authorization request and return valid authorization
366+ state for later response generation.
351367 For more information see
352368 `RFC6749 section 4.1.1 <https://tools.ietf.org/html/rfc6749#section-4.1.1>`_.
353369
@@ -365,8 +381,10 @@ async def create_authorization_response(
365381 async def authorize(request: fastapi.Request) -> fastapi.Response:
366382 # Converts a fastapi.Request to an aioauth.Request.
367383 oauth2_request: aioauth.Request = await to_oauth2_request(request)
384+ # Validate the oauth request
385+ auth_state: aioauth.AuthState = await server.validate_authorization_request(oauth2_request)
368386 # Creates the response via this function call.
369- oauth2_response: aioauth.Response = await server.create_authorization_response(oauth2_request )
387+ oauth2_response: aioauth.Response = await server.create_authorization_response(auth_state )
370388 # Converts an aioauth.Response to a fastapi.Response.
371389 response: fastapi.Response = await to_fastapi_response(oauth2_response)
372390 return response
@@ -375,25 +393,12 @@ async def authorize(request: fastapi.Request) -> fastapi.Response:
375393 request: An :py:class:`aioauth.requests.Request` object.
376394
377395 Returns:
378- response : An :py:class:`aioauth.responses.Response ` object.
396+ state : An :py:class:`aioauth.server.AuthState ` object.
379397 """
380398 self .validate_request (request , ["GET" , "POST" ])
381399
382400 response_type_list = enforce_list (request .query .response_type )
383401 response_type_classes = set ()
384-
385- # Combined responses
386- responses = {}
387-
388- # URI fragment
389- fragment = {}
390-
391- # URI query params
392- query = {}
393-
394- # Response content
395- content = {}
396-
397402 state = request .query .state
398403
399404 if not response_type_list :
@@ -403,9 +408,6 @@ async def authorize(request: fastapi.Request) -> fastapi.Response:
403408 state = state ,
404409 )
405410
406- if state :
407- responses ["state" ] = state
408-
409411 for response_type in response_type_list :
410412 ResponseTypeClass = self .response_types .get (response_type )
411413 if ResponseTypeClass :
@@ -414,9 +416,79 @@ async def authorize(request: fastapi.Request) -> fastapi.Response:
414416 if not response_type_classes :
415417 raise UnsupportedResponseTypeError [UserType ](request = request , state = state )
416418
419+ auth_state = AuthorizationState (request , response_type_list , grants = [])
417420 for ResponseTypeClass in response_type_classes :
418421 response_type = ResponseTypeClass (storage = self .storage )
419- client = await response_type .validate_request (request )
422+ client = await response_type .validate_request (request , skip_user = True )
423+ auth_state .grants .append ((response_type , client ))
424+ return auth_state
425+
426+ @catch_errors_and_unavailability (
427+ skip_redirect_on_exc = (
428+ MethodNotAllowedError ,
429+ InvalidClientError ,
430+ InvalidRedirectURIError ,
431+ )
432+ )
433+ async def create_authorization_response (
434+ self ,
435+ auth_state : AuthorizationState [UserType ],
436+ ) -> Response :
437+ """
438+ Endpoint to interact with the resource owner and obtain an
439+ authorization grant.
440+ Create an authorization response after validation.
441+ For more information see
442+ `RFC6749 section 4.1.1 <https://tools.ietf.org/html/rfc6749#section-4.1.1>`_.
443+
444+ Example:
445+ Below is an example utilizing FastAPI as the server framework.
446+ .. code-block:: python
447+
448+ from aioauth.fastapi.utils import to_oauth2_request, to_fastapi_response
449+
450+ @app.post("/authorize")
451+ async def authorize(request: fastapi.Request) -> fastapi.Response:
452+ # Converts a fastapi.Request to an aioauth.Request.
453+ oauth2_request: aioauth.Request = await to_oauth2_request(request)
454+ # Validate the oauth request
455+ auth_state: aioauth.AuthState = await server.validate_authorization_request(oauth2_request)
456+ # Creates the response via this function call.
457+ oauth2_response: aioauth.Response = await server.create_authorization_response(auth_state)
458+ # Converts an aioauth.Response to a fastapi.Response.
459+ response: fastapi.Response = await to_fastapi_response(oauth2_response)
460+ return response
461+
462+ Args:
463+ auth_state: An :py:class:`aioauth.server.AuthState` object.
464+
465+ Returns:
466+ response: An :py:class:`aioauth.responses.Response` object.
467+ """
468+ request = auth_state .request
469+ state = auth_state .request .query .state
470+ response_type_list = auth_state .response_type_list
471+ if request .user :
472+ raise InvalidClientError [UserType ](
473+ request = request , description = "User is not authorized" , state = state
474+ )
475+
476+ # Combined responses
477+ responses = {}
478+
479+ # URI fragment
480+ fragment = {}
481+
482+ # URI query params
483+ query = {}
484+
485+ # Response content
486+ content = {}
487+
488+ if state :
489+ responses ["state" ] = state
490+
491+ for response_type , client in auth_state .grants :
420492 response = await response_type .create_authorization_response (
421493 request , client
422494 )
0 commit comments