Skip to content

Commit ee8cf16

Browse files
Guillaume Pujolguillp
authored andcommitted
refactor serializers and enums into their own submodules
1 parent f1bbf81 commit ee8cf16

File tree

13 files changed

+457
-311
lines changed

13 files changed

+457
-311
lines changed

requests_oauth2client/__init__.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,13 @@
1818
)
1919
from .authorization_request import (
2020
AuthorizationRequest,
21-
AuthorizationRequestSerializer,
2221
AuthorizationResponse,
23-
CodeChallengeMethods,
2422
InvalidCodeVerifierParam,
2523
InvalidMaxAgeParam,
2624
MissingIssuerParam,
2725
PkceUtils,
2826
RequestParameterAuthorizationRequest,
2927
RequestUriParameterAuthorizationRequest,
30-
ResponseTypes,
3128
UnsupportedCodeChallengeMethod,
3229
UnsupportedResponseTypeParam,
3330
)
@@ -36,8 +33,6 @@
3633
BackChannelAuthenticationResponse,
3734
)
3835
from .client import (
39-
Endpoints,
40-
GrantTypes,
4136
InvalidAcrValuesParam,
4237
InvalidBackchannelAuthenticationRequestHintParam,
4338
InvalidDiscoveryDocument,
@@ -93,6 +88,7 @@
9388
RepeatedDPoPNonce,
9489
validate_dpop_proof,
9590
)
91+
from .enums import CodeChallengeMethods, Endpoints, GrantTypes, ResponseTypes
9692
from .exceptions import (
9793
AccessDenied,
9894
AccountSelectionRequired,
@@ -135,9 +131,9 @@
135131
from .polling import (
136132
BaseTokenEndpointPollingJob,
137133
)
134+
from .serializers import AuthorizationRequestSerializer, BearerTokenSerializer
138135
from .tokens import (
139136
BearerToken,
140-
BearerTokenSerializer,
141137
ExpiredAccessToken,
142138
ExpiredIdToken,
143139
IdToken,

requests_oauth2client/authorization_request.py

Lines changed: 2 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,16 @@
44

55
import re
66
import secrets
7-
from enum import Enum
87
from functools import cached_property
9-
from typing import TYPE_CHECKING, Any, Callable, ClassVar
8+
from typing import TYPE_CHECKING, Any, ClassVar
109

1110
from attrs import asdict, field, fields, frozen
1211
from binapy import BinaPy
1312
from furl import furl # type: ignore[import-untyped]
1413
from jwskate import JweCompact, Jwk, Jwt, SignatureAlgs, SignedJwt
1514

1615
from .dpop import DPoPKey
16+
from .enums import CodeChallengeMethods, ResponseTypes
1717
from .exceptions import (
1818
AuthorizationResponseError,
1919
ConsentRequired,
@@ -32,34 +32,6 @@
3232
from datetime import datetime
3333

3434

35-
class ResponseTypes(str, Enum):
36-
"""All standardised `response_type` values.
37-
38-
Note that you should always use `code`. All other values are deprecated.
39-
40-
"""
41-
42-
CODE = "code"
43-
NONE = "none"
44-
TOKEN = "token"
45-
IDTOKEN = "id_token"
46-
CODE_IDTOKEN = "code id_token"
47-
CODE_TOKEN = "code token"
48-
CODE_IDTOKEN_TOKEN = "code id_token token"
49-
IDTOKEN_TOKEN = "id_token token"
50-
51-
52-
class CodeChallengeMethods(str, Enum):
53-
"""All standardised `code_challenge_method` values.
54-
55-
You should always use `S256`.
56-
57-
"""
58-
59-
S256 = "S256"
60-
plain = "plain"
61-
62-
6335
class UnsupportedCodeChallengeMethod(ValueError):
6436
"""Raised when an unsupported `code_challenge_method` is provided."""
6537

@@ -908,92 +880,3 @@ def __getattr__(self, item: str) -> Any:
908880
def __repr__(self) -> str:
909881
"""Return the Authorization Request URI, as a `str`."""
910882
return self.uri
911-
912-
913-
class AuthorizationRequestSerializer:
914-
"""(De)Serializer for `AuthorizationRequest` instances.
915-
916-
You might need to store pending authorization requests in session, either server-side or client- side. This class is
917-
here to help you do that.
918-
919-
"""
920-
921-
def __init__(
922-
self,
923-
dumper: Callable[[AuthorizationRequest], str] | None = None,
924-
loader: Callable[[str], AuthorizationRequest] | None = None,
925-
) -> None:
926-
self.dumper = dumper or self.default_dumper
927-
self.loader = loader or self.default_loader
928-
929-
@staticmethod
930-
def default_dumper(azr: AuthorizationRequest) -> str:
931-
"""Provide a default dumper implementation.
932-
933-
Serialize an AuthorizationRequest as JSON, then compress with deflate, then encodes as
934-
base64url.
935-
936-
Args:
937-
azr: the `AuthorizationRequest` to serialize
938-
939-
Returns:
940-
the serialized value
941-
942-
"""
943-
d = asdict(azr)
944-
if azr.dpop_key:
945-
d["dpop_key"]["private_key"] = azr.dpop_key.private_key.to_dict()
946-
d.update(**d.pop("kwargs", {}))
947-
return BinaPy.serialize_to("json", d).to("deflate").to("b64u").ascii()
948-
949-
@staticmethod
950-
def default_loader(
951-
serialized: str,
952-
azr_class: type[AuthorizationRequest] = AuthorizationRequest,
953-
) -> AuthorizationRequest:
954-
"""Provide a default deserializer implementation.
955-
956-
This does the opposite operations than `default_dumper`.
957-
958-
Args:
959-
serialized: the serialized AuthorizationRequest
960-
azr_class: the class to deserialize the Authorization Request to
961-
962-
Returns:
963-
an AuthorizationRequest
964-
965-
"""
966-
args = BinaPy(serialized).decode_from("b64u").decode_from("deflate").parse_from("json")
967-
968-
if dpop_key := args.get("dpop_key"):
969-
dpop_key["private_key"] = Jwk(dpop_key["private_key"])
970-
dpop_key.pop("jti_generator", None)
971-
dpop_key.pop("iat_generator", None)
972-
dpop_key.pop("dpop_token_class", None)
973-
args["dpop_key"] = DPoPKey(**dpop_key)
974-
975-
return azr_class(**args)
976-
977-
def dumps(self, azr: AuthorizationRequest) -> str:
978-
"""Serialize and compress a given AuthorizationRequest for easier storage.
979-
980-
Args:
981-
azr: an AuthorizationRequest to serialize
982-
983-
Returns:
984-
the serialized AuthorizationRequest, as a str
985-
986-
"""
987-
return self.dumper(azr)
988-
989-
def loads(self, serialized: str) -> AuthorizationRequest:
990-
"""Deserialize a serialized AuthorizationRequest.
991-
992-
Args:
993-
serialized: the serialized AuthorizationRequest
994-
995-
Returns:
996-
the deserialized AuthorizationRequest
997-
998-
"""
999-
return self.loader(serialized)

requests_oauth2client/client.py

Lines changed: 2 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
from __future__ import annotations
44

55
import warnings
6-
from enum import Enum
76
from typing import TYPE_CHECKING, Any, Callable, ClassVar, TypeVar
87

98
import requests
@@ -14,16 +13,15 @@
1413
from .authorization_request import (
1514
AuthorizationRequest,
1615
AuthorizationResponse,
17-
CodeChallengeMethods,
1816
MissingIssuerParam,
1917
RequestUriParameterAuthorizationRequest,
20-
ResponseTypes,
2118
)
2219
from .backchannel_authentication import BackChannelAuthenticationResponse
2320
from .client_authentication import ClientSecretPost, PrivateKeyJwt, client_auth_factory
2421
from .device_authorization import DeviceAuthorizationResponse
2522
from .discovery import oidc_discovery_document_url
2623
from .dpop import DPoPKey, DPoPToken, InvalidDPoPAlg, MissingDPoPNonce, RepeatedDPoPNonce
24+
from .enums import CodeChallengeMethods, Endpoints, GrantTypes, ResponseTypes, TokenType
2725
from .exceptions import (
2826
AccessDenied,
2927
AuthorizationPending,
@@ -50,7 +48,7 @@
5048
UnsupportedTokenType,
5149
UseDPoPNonce,
5250
)
53-
from .tokens import BearerToken, IdToken, TokenResponse, TokenType
51+
from .tokens import BearerToken, IdToken, TokenResponse
5452
from .utils import InvalidUri, validate_endpoint_uri, validate_issuer_uri
5553

5654
if TYPE_CHECKING:
@@ -169,44 +167,13 @@ def __init__(self, message: str, discovery_document: dict[str, Any]) -> None:
169167
self.discovery_document = discovery_document
170168

171169

172-
class Endpoints(str, Enum):
173-
"""All standardised OAuth 2.0 and extensions endpoints.
174-
175-
If an endpoint is not mentioned here, then its usage is not supported by OAuth2Client.
176-
177-
"""
178-
179-
TOKEN = "token_endpoint"
180-
AUTHORIZATION = "authorization_endpoint"
181-
BACKCHANNEL_AUTHENTICATION = "backchannel_authentication_endpoint"
182-
DEVICE_AUTHORIZATION = "device_authorization_endpoint"
183-
INTROSPECTION = "introspection_endpoint"
184-
REVOCATION = "revocation_endpoint"
185-
PUSHED_AUTHORIZATION_REQUEST = "pushed_authorization_request_endpoint"
186-
JWKS = "jwks_uri"
187-
USER_INFO = "userinfo_endpoint"
188-
189-
190170
class MissingEndpointUri(AttributeError):
191171
"""Raised when a required endpoint uri is not known."""
192172

193173
def __init__(self, endpoint: str) -> None:
194174
super().__init__(f"No '{endpoint}' defined for this client.")
195175

196176

197-
class GrantTypes(str, Enum):
198-
"""An enum of standardized `grant_type` values."""
199-
200-
CLIENT_CREDENTIALS = "client_credentials"
201-
AUTHORIZATION_CODE = "authorization_code"
202-
REFRESH_TOKEN = "refresh_token"
203-
RESOURCE_OWNER_PASSWORD = "password"
204-
TOKEN_EXCHANGE = "urn:ietf:params:oauth:grant-type:token-exchange"
205-
JWT_BEARER = "urn:ietf:params:oauth:grant-type:jwt-bearer"
206-
CLIENT_INITIATED_BACKCHANNEL_AUTHENTICATION = "urn:openid:params:grant-type:ciba"
207-
DEVICE_CODE = "urn:ietf:params:oauth:grant-type:device_code"
208-
209-
210177
@frozen(init=False)
211178
class OAuth2Client:
212179
"""An OAuth 2.x Client that can send requests to an OAuth 2.x Authorization Server.

requests_oauth2client/deprecated.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"""Mark a class as deprecated.
44
55
https://stackoverflow.com/a/52087847
6+
67
"""
78

89
from warnings import warn

requests_oauth2client/dpop.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
from requests import codes
1616
from typing_extensions import Self
1717

18-
from .tokens import AccessTokenTypes, BearerToken, IdToken, id_token_converter
18+
from .enums import AccessTokenTypes
19+
from .tokens import BearerToken, IdToken, id_token_converter
1920
from .utils import accepts_expires_in
2021

2122
if TYPE_CHECKING:
@@ -205,8 +206,8 @@ class DPoPKey:
205206

206207
alg: str = field(on_setattr=setters.frozen)
207208
private_key: jwskate.Jwk = field(on_setattr=setters.frozen, repr=False)
208-
jti_generator: Callable[[], str] = field(on_setattr=setters.frozen, repr=False)
209-
iat_generator: Callable[[], int] = field(on_setattr=setters.frozen, repr=False)
209+
jti_generator: Callable[[], str] = field(on_setattr=setters.frozen, repr=False, eq=False)
210+
iat_generator: Callable[[], int] = field(on_setattr=setters.frozen, repr=False, eq=False)
210211
jwt_typ: str = field(on_setattr=setters.frozen, repr=False)
211212
dpop_token_class: type[DPoPToken] = field(on_setattr=setters.frozen, repr=False)
212213
as_nonce: str | None

requests_oauth2client/enums.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
"""Contains enumerations of standardised OAuth-related parameters and values.
2+
3+
Most are taken from https://www.iana.org/assignments/oauth-parameters/oauth-parameters.xhtml .
4+
5+
"""
6+
7+
from __future__ import annotations
8+
9+
from enum import Enum
10+
11+
12+
class AccessTokenTypes(str, Enum):
13+
"""An enum of standardised `access_token` types."""
14+
15+
BEARER = "Bearer"
16+
DPOP = "DPoP"
17+
18+
19+
class CodeChallengeMethods(str, Enum):
20+
"""All standardised `code_challenge_method` values.
21+
22+
You should always use `S256`.
23+
24+
"""
25+
26+
S256 = "S256"
27+
plain = "plain"
28+
29+
30+
class Endpoints(str, Enum):
31+
"""All standardised OAuth 2.0 and extensions endpoints.
32+
33+
If an endpoint is not mentioned here, then its usage is not supported by OAuth2Client.
34+
35+
"""
36+
37+
TOKEN = "token_endpoint"
38+
AUTHORIZATION = "authorization_endpoint"
39+
BACKCHANNEL_AUTHENTICATION = "backchannel_authentication_endpoint"
40+
DEVICE_AUTHORIZATION = "device_authorization_endpoint"
41+
INTROSPECTION = "introspection_endpoint"
42+
REVOCATION = "revocation_endpoint"
43+
PUSHED_AUTHORIZATION_REQUEST = "pushed_authorization_request_endpoint"
44+
JWKS = "jwks_uri"
45+
USER_INFO = "userinfo_endpoint"
46+
47+
48+
class GrantTypes(str, Enum):
49+
"""An enum of standardized `grant_type` values."""
50+
51+
CLIENT_CREDENTIALS = "client_credentials"
52+
AUTHORIZATION_CODE = "authorization_code"
53+
REFRESH_TOKEN = "refresh_token"
54+
RESOURCE_OWNER_PASSWORD = "password"
55+
TOKEN_EXCHANGE = "urn:ietf:params:oauth:grant-type:token-exchange"
56+
JWT_BEARER = "urn:ietf:params:oauth:grant-type:jwt-bearer"
57+
CLIENT_INITIATED_BACKCHANNEL_AUTHENTICATION = "urn:openid:params:grant-type:ciba"
58+
DEVICE_CODE = "urn:ietf:params:oauth:grant-type:device_code"
59+
60+
61+
class ResponseTypes(str, Enum):
62+
"""All standardised `response_type` values.
63+
64+
Note that you should always use `code`. All other values are deprecated.
65+
66+
"""
67+
68+
CODE = "code"
69+
NONE = "none"
70+
TOKEN = "token"
71+
IDTOKEN = "id_token"
72+
CODE_IDTOKEN = "code id_token"
73+
CODE_TOKEN = "code token"
74+
CODE_IDTOKEN_TOKEN = "code id_token token"
75+
IDTOKEN_TOKEN = "id_token token"
76+
77+
78+
class TokenType(str, Enum):
79+
"""An enum of standardised `token_type` values."""
80+
81+
ACCESS_TOKEN = "access_token"
82+
REFRESH_TOKEN = "refresh_token"
83+
ID_TOKEN = "id_token"

requests_oauth2client/flask/auth.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66

77
from flask import session
88

9+
from requests_oauth2client import BearerTokenSerializer
910
from requests_oauth2client.auth import OAuth2ClientCredentialsAuth
10-
from requests_oauth2client.tokens import BearerToken, BearerTokenSerializer
11+
from requests_oauth2client.tokens import BearerToken
1112

1213

1314
class FlaskSessionAuthMixin:

0 commit comments

Comments
 (0)