1212
1313from abc import ABC , abstractmethod
1414from datetime import datetime , timezone
15- from typing import TYPE_CHECKING , Any , Callable , Generic , TypeVar , override
15+ from typing import TYPE_CHECKING , Any , Callable , ClassVar , Generic , TypeVar , override
1616
1717import jwskate
1818from attr import asdict , field , frozen
1919from binapy import BinaPy
20- from jwskate import Jwk
2120
22- from . import RequestParameterAuthorizationRequest , RequestUriParameterAuthorizationRequest
23- from .authorization_request import AuthorizationRequest
21+ from .authorization_request import (
22+ AuthorizationRequest ,
23+ RequestParameterAuthorizationRequest ,
24+ RequestUriParameterAuthorizationRequest ,
25+ )
2426from .dpop import DPoPKey , DPoPToken
2527from .tokens import BearerToken
2628
@@ -101,8 +103,8 @@ def get_class(self, args: Mapping[str, Any]) -> type[BearerToken]:
101103 "dpop" : DPoPToken ,
102104 }.get (token_type .lower (), BearerToken )
103105
104- @staticmethod
105- def default_dumper (token : BearerToken ) -> str :
106+ @classmethod
107+ def default_dumper (cls , token : BearerToken ) -> str :
106108 """Serialize a token as JSON, then compress with deflate, then encodes as base64url.
107109
108110 Args:
@@ -123,8 +125,10 @@ def default_dumper(token: BearerToken) -> str:
123125 BinaPy .serialize_to ("json" , {k : w for k , w in d .items () if w is not None }).to ("deflate" ).to ("b64u" ).ascii ()
124126 )
125127
126- @staticmethod
127- def default_loader (serialized : str , get_class : Callable [[Mapping [str , Any ]], type [BearerToken ]]) -> BearerToken :
128+ @classmethod
129+ def default_loader (
130+ cls , serialized : str , get_class : Callable [[Mapping [str , Any ]], type [BearerToken ]]
131+ ) -> BearerToken :
128132 """Deserialize a BearerToken.
129133
130134 This does the opposite operations than `default_dumper`.
@@ -151,6 +155,48 @@ def default_loader(serialized: str, get_class: Callable[[Mapping[str, Any]], typ
151155 return token_class (** args )
152156
153157
158+ @frozen
159+ class DPoPKeySerializer (Serializer [DPoPKey ]):
160+ """A (de)serializer for `DPoPKey` instances."""
161+
162+ dumper : Callable [[DPoPKey ], str ] = field (factory = lambda : DPoPKeySerializer .default_dumper )
163+ loader : Callable [[str , Callable [[Mapping [str , Any ]], type [DPoPKey ]]], DPoPKey ] = field (
164+ factory = lambda : DPoPKeySerializer .default_loader
165+ )
166+
167+ @override
168+ def get_class (self , args : Mapping [str , Any ]) -> type [DPoPKey ]:
169+ return DPoPKey
170+
171+ @classmethod
172+ def default_dumper (cls , dpop_key : DPoPKey ) -> str :
173+ """Provide a default dumper implementation.
174+
175+ This will not serialize jti_generator, iat_generator, and dpop_token_class!
176+
177+ """
178+ d = dpop_key .private_key .to_dict ()
179+ d .pop ("jti_generator" , None )
180+ d .pop ("iat_generator" , None )
181+ d .pop ("dpop_token_class" , None )
182+ return BinaPy .serialize_to ("json" , d ).to ("deflate" ).to ("b64u" ).ascii ()
183+
184+ @classmethod
185+ def default_loader (
186+ cls ,
187+ serialized : str ,
188+ get_class : Callable [[Mapping [str , Any ]], type [DPoPKey ]],
189+ ) -> DPoPKey :
190+ """Provide a default deserializer implementation.
191+
192+ This will not deserialize iat_generator, iat_generator, and dpop_token_class!
193+
194+ """
195+ private_key = BinaPy (serialized ).decode_from ("b64u" ).decode_from ("deflate" ).parse_from ("json" )
196+ dpop_class = get_class ({})
197+ return dpop_class (private_key = private_key )
198+
199+
154200@frozen
155201class AuthorizationRequestSerializer (
156202 Serializer [AuthorizationRequest | RequestParameterAuthorizationRequest | RequestUriParameterAuthorizationRequest ]
@@ -180,6 +226,8 @@ class AuthorizationRequestSerializer(
180226 AuthorizationRequest | RequestParameterAuthorizationRequest | RequestUriParameterAuthorizationRequest ,
181227 ] = field (factory = lambda : AuthorizationRequestSerializer .default_loader )
182228
229+ dpop_key_serializer : ClassVar [Serializer [DPoPKey ]] = DPoPKeySerializer ()
230+
183231 @override
184232 def get_class (
185233 self , args : Mapping [str , Any ]
@@ -190,8 +238,9 @@ def get_class(
190238 return RequestUriParameterAuthorizationRequest
191239 return AuthorizationRequest
192240
193- @staticmethod
241+ @classmethod
194242 def default_dumper (
243+ cls ,
195244 azr : AuthorizationRequest | RequestParameterAuthorizationRequest | RequestUriParameterAuthorizationRequest ,
196245 ) -> str :
197246 """Provide a default dumper implementation.
@@ -208,12 +257,13 @@ def default_dumper(
208257 """
209258 d = asdict (azr )
210259 if azr .dpop_key :
211- d ["dpop_key" ][ "private_key" ] = azr . dpop_key . private_key . to_dict ( )
260+ d ["dpop_key" ] = cls . dpop_key_serializer . dumps ( azr . dpop_key )
212261 d .update (** d .pop ("kwargs" , {}))
213262 return BinaPy .serialize_to ("json" , d ).to ("deflate" ).to ("b64u" ).ascii ()
214263
215- @staticmethod
264+ @classmethod
216265 def default_loader (
266+ cls ,
217267 serialized : str ,
218268 get_class : Callable [
219269 [Mapping [str , Any ]],
@@ -234,55 +284,9 @@ def default_loader(
234284 """
235285 args = BinaPy (serialized ).decode_from ("b64u" ).decode_from ("deflate" ).parse_from ("json" )
236286
237- if dpop_key := args .get ("dpop_key" ):
238- dpop_key ["private_key" ] = Jwk (dpop_key ["private_key" ])
239- dpop_key .pop ("jti_generator" , None )
240- dpop_key .pop ("iat_generator" , None )
241- dpop_key .pop ("dpop_token_class" , None )
242- args ["dpop_key" ] = DPoPKey (** dpop_key )
287+ if args ["dpop_key" ]:
288+ args ["dpop_key" ] = cls .dpop_key_serializer .loads (args ["dpop_key" ])
243289
244290 azr_class = get_class (args )
245291
246292 return azr_class (** args )
247-
248-
249- @frozen
250- class DPoPKeySerializer (Serializer [DPoPKey ]):
251- """A (de)serializer for `DPoPKey` instances."""
252-
253- dumper : Callable [[DPoPKey ], str ] = field (factory = lambda : DPoPKeySerializer .default_dumper )
254- loader : Callable [[str , Callable [[Mapping [str , Any ]], type [DPoPKey ]]], DPoPKey ] = field (
255- factory = lambda : DPoPKeySerializer .default_loader
256- )
257-
258- @override
259- def get_class (self , args : Mapping [str , Any ]) -> type [DPoPKey ]:
260- return DPoPKey
261-
262- @staticmethod
263- def default_dumper (dpop_key : DPoPKey ) -> str :
264- """Provide a default dumper implementation.
265-
266- This will not serialize jti_generator, iat_generator, and dpop_token_class!
267-
268- """
269- d = dpop_key .private_key .to_dict ()
270- d .pop ("jti_generator" , None )
271- d .pop ("iat_generator" , None )
272- d .pop ("dpop_token_class" , None )
273- return BinaPy .serialize_to ("json" , d ).to ("deflate" ).to ("b64u" ).ascii ()
274-
275- @staticmethod
276- def default_loader (
277- serialized : str ,
278- get_class : Callable [[Mapping [str , Any ]], type [DPoPKey ]],
279- ) -> DPoPKey :
280- """Provide a default deserializer implementation.
281-
282- This will not deserialize iat_generator, iat_generator, and dpop_token_class!
283-
284- """
285- args = BinaPy (serialized ).decode_from ("b64u" ).decode_from ("deflate" ).parse_from ("json" )
286- args ["private_key" ] = Jwk (args ["private_key" ])
287- cls = get_class (args )
288- return cls (** args )
0 commit comments