okta-client-python 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. okta_client/__init__.py +20 -0
  2. okta_client/authfoundation/__init__.py +197 -0
  3. okta_client/authfoundation/authentication.py +247 -0
  4. okta_client/authfoundation/coalesced_result.py +113 -0
  5. okta_client/authfoundation/codable.py +72 -0
  6. okta_client/authfoundation/expires.py +49 -0
  7. okta_client/authfoundation/key_provider.py +130 -0
  8. okta_client/authfoundation/networking/__init__.py +56 -0
  9. okta_client/authfoundation/networking/body.py +46 -0
  10. okta_client/authfoundation/networking/client.py +200 -0
  11. okta_client/authfoundation/networking/types.py +293 -0
  12. okta_client/authfoundation/oauth2/__init__.py +104 -0
  13. okta_client/authfoundation/oauth2/claims.py +44 -0
  14. okta_client/authfoundation/oauth2/client.py +402 -0
  15. okta_client/authfoundation/oauth2/client_authorization.py +172 -0
  16. okta_client/authfoundation/oauth2/config.py +298 -0
  17. okta_client/authfoundation/oauth2/errors.py +32 -0
  18. okta_client/authfoundation/oauth2/jwt_bearer_claims.py +59 -0
  19. okta_client/authfoundation/oauth2/jwt_bearer_utils.py +30 -0
  20. okta_client/authfoundation/oauth2/jwt_context.py +52 -0
  21. okta_client/authfoundation/oauth2/jwt_token.py +214 -0
  22. okta_client/authfoundation/oauth2/models.py +198 -0
  23. okta_client/authfoundation/oauth2/parameters.py +36 -0
  24. okta_client/authfoundation/oauth2/refresh_token.py +165 -0
  25. okta_client/authfoundation/oauth2/request_protocols.py +174 -0
  26. okta_client/authfoundation/oauth2/requests/__init__.py +37 -0
  27. okta_client/authfoundation/oauth2/requests/introspect.py +50 -0
  28. okta_client/authfoundation/oauth2/requests/jwks.py +44 -0
  29. okta_client/authfoundation/oauth2/requests/oauth_authorization_server.py +44 -0
  30. okta_client/authfoundation/oauth2/requests/openid_configuration.py +47 -0
  31. okta_client/authfoundation/oauth2/requests/revoke.py +54 -0
  32. okta_client/authfoundation/oauth2/requests/user_info.py +37 -0
  33. okta_client/authfoundation/oauth2/utils.py +25 -0
  34. okta_client/authfoundation/oauth2/validation_protocols.py +33 -0
  35. okta_client/authfoundation/oauth2/validator_registry.py +64 -0
  36. okta_client/authfoundation/oauth2/validators/token_hash.py +37 -0
  37. okta_client/authfoundation/oauth2/validators/token_validator.py +26 -0
  38. okta_client/authfoundation/time_coordinator.py +57 -0
  39. okta_client/authfoundation/token.py +201 -0
  40. okta_client/authfoundation/user_agent.py +80 -0
  41. okta_client/authfoundation/utils.py +63 -0
  42. okta_client/browser_signin/__init__.py +11 -0
  43. okta_client/directauth/__init__.py +11 -0
  44. okta_client/oauth2auth/__init__.py +63 -0
  45. okta_client/oauth2auth/authorization_code.py +594 -0
  46. okta_client/oauth2auth/cross_app.py +626 -0
  47. okta_client/oauth2auth/jwt_bearer.py +182 -0
  48. okta_client/oauth2auth/resource_owner.py +159 -0
  49. okta_client/oauth2auth/token_exchange.py +380 -0
  50. okta_client/oauth2auth/utils.py +87 -0
  51. okta_client/py.typed +0 -0
  52. okta_client_python-0.1.0.dist-info/METADATA +936 -0
  53. okta_client_python-0.1.0.dist-info/RECORD +56 -0
  54. okta_client_python-0.1.0.dist-info/WHEEL +5 -0
  55. okta_client_python-0.1.0.dist-info/licenses/LICENSE +171 -0
  56. okta_client_python-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,293 @@
1
+ # The Okta software accompanied by this notice is provided pursuant to the following terms:
2
+ # Copyright © 2026-Present, Okta, Inc.
3
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
4
+ # License.
5
+ # You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
6
+ # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
7
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8
+ # See the License for the specific language governing permissions and limitations under the License.
9
+ # coding: utf-8
10
+
11
+ from __future__ import annotations
12
+
13
+ from collections.abc import Iterator, Mapping, Sequence
14
+ from dataclasses import dataclass
15
+ from datetime import datetime
16
+ from enum import Enum
17
+ from typing import (
18
+ TYPE_CHECKING,
19
+ Any,
20
+ Generic,
21
+ Protocol,
22
+ TypeVar,
23
+ cast,
24
+ runtime_checkable,
25
+ )
26
+
27
+ if TYPE_CHECKING:
28
+ from .client import APIClient
29
+
30
+ T = TypeVar("T")
31
+ ListenerT = TypeVar("ListenerT")
32
+
33
+
34
+ class APIContentType(str, Enum):
35
+ """Common content types for request and response bodies."""
36
+ JSON = "application/json"
37
+ FORM_URLENCODED = "application/x-www-form-urlencoded"
38
+ TEXT = "text/plain"
39
+
40
+
41
+ class APIRequestMethod(str, Enum):
42
+ """HTTP request methods."""
43
+ GET = "GET"
44
+ POST = "POST"
45
+ PUT = "PUT"
46
+ PATCH = "PATCH"
47
+ DELETE = "DELETE"
48
+
49
+
50
+ @runtime_checkable
51
+ class RequestValueConvertible(Protocol):
52
+ """Protocol for values that can serialize themselves to a string."""
53
+
54
+ def to_request_value(self) -> str:
55
+ """Return a string representation for request serialization."""
56
+ ...
57
+
58
+
59
+ RequestValue = str | int | float | bool | Sequence[str] | RequestValueConvertible | None
60
+
61
+
62
+ @runtime_checkable
63
+ class APIRequestBody(Protocol):
64
+ """Protocol for requests that supply body parameters."""
65
+ @property
66
+ def body_parameters(self) -> Mapping[str, RequestValue]:
67
+ """Body parameters to serialize based on content type."""
68
+ ...
69
+
70
+
71
+ @runtime_checkable
72
+ class APIAuthorization(Protocol):
73
+ """Protocol for objects that authorize an outgoing HTTP request."""
74
+ def authorize(self, request: HTTPRequest) -> HTTPRequest:
75
+ """Apply authorization to the request and return it."""
76
+ ...
77
+
78
+
79
+ class APIParsingContext(Protocol):
80
+ """Marker protocol for parsing hints."""
81
+
82
+
83
+ @dataclass(frozen=True)
84
+ class APIRateLimit:
85
+ """Rate-limit metadata extracted from responses."""
86
+ limit: int
87
+ remaining: int
88
+ reset: datetime
89
+
90
+
91
+ class BackoffStrategy(Protocol):
92
+ """Protocol for retry backoff strategies."""
93
+ def delay_for_attempt(self, attempt: int) -> float:
94
+ """Return a delay in seconds for the given attempt."""
95
+ ...
96
+
97
+
98
+ @dataclass(frozen=True)
99
+ class APIRetry:
100
+ """Retry policy returned by listeners or defaults."""
101
+ kind: str
102
+ maximum_count: int | None = None
103
+ backoff_strategy: BackoffStrategy | None = None
104
+
105
+ @classmethod
106
+ def do_not_retry(cls) -> APIRetry:
107
+ """Return a policy that disables retries."""
108
+ return cls(kind="do_not_retry")
109
+
110
+ @classmethod
111
+ def retry(cls, maximum_count: int, backoff_strategy: BackoffStrategy | None = None) -> APIRetry:
112
+ """Return a policy that retries up to a maximum count."""
113
+ return cls(kind="retry", maximum_count=maximum_count, backoff_strategy=backoff_strategy)
114
+
115
+ @classmethod
116
+ def default(cls) -> APIRetry:
117
+ """Return the SDK default retry policy."""
118
+ return cls(kind="default")
119
+
120
+
121
+ @dataclass(frozen=True)
122
+ class RawResponse:
123
+ """Raw response data returned by the network interface."""
124
+ status_code: int
125
+ headers: Mapping[str, str]
126
+ body: bytes
127
+
128
+
129
+ @dataclass
130
+ class HTTPRequest:
131
+ """HTTP request envelope used by the network interface."""
132
+ method: APIRequestMethod
133
+ url: str
134
+ headers: dict[str, str]
135
+ body: bytes | None
136
+ timeout: float | None
137
+
138
+
139
+ @dataclass(frozen=True)
140
+ class APIResponse(Generic[T]):
141
+ """Parsed response payload plus response metadata."""
142
+ result: T
143
+ status_code: int
144
+ headers: Mapping[str, str]
145
+ request_id: str | None = None
146
+ rate_limit: APIRateLimit | None = None
147
+ links: Mapping[str, str] | None = None
148
+
149
+
150
+ class APIRequest(Generic[T], Protocol):
151
+ """Protocol describing a transport-agnostic API request."""
152
+ @property
153
+ def http_method(self) -> APIRequestMethod:
154
+ ...
155
+
156
+ @property
157
+ def url(self) -> str:
158
+ ...
159
+
160
+ @property
161
+ def query(self) -> Mapping[str, RequestValue] | None:
162
+ ...
163
+
164
+ @property
165
+ def headers(self) -> Mapping[str, RequestValue] | None:
166
+ ...
167
+
168
+ @property
169
+ def accepts_type(self) -> APIContentType | None:
170
+ ...
171
+
172
+ @property
173
+ def content_type(self) -> APIContentType | None:
174
+ ...
175
+
176
+ @property
177
+ def authorization(self) -> APIAuthorization | None:
178
+ ...
179
+
180
+ @property
181
+ def timeout(self) -> float | None:
182
+ ...
183
+
184
+ def body(self) -> bytes | None:
185
+ """Return the serialized request body, if any."""
186
+ ...
187
+
188
+ def parse_response(self, response: RawResponse, parsing_context: APIParsingContext | None = None) -> T:
189
+ """Parse the raw response into a typed result."""
190
+ ...
191
+
192
+ def build_http_request(self, client: APIClient) -> HTTPRequest:
193
+ """Build a platform HTTP request using the provided client."""
194
+ return client.build_http_request(cast("APIRequest[Any]", self))
195
+
196
+ def send(self, client: APIClient, parsing_context: APIParsingContext | None = None) -> APIResponse[T]:
197
+ """Send the request using the provided client."""
198
+ return client.send(cast("APIRequest[T]", self), parsing_context=parsing_context)
199
+
200
+ class BaseAPIRequest(Generic[T]):
201
+ """Base request with standard optional defaults."""
202
+
203
+ @property
204
+ def query(self) -> Mapping[str, RequestValue] | None:
205
+ return None
206
+
207
+ @property
208
+ def headers(self) -> Mapping[str, RequestValue] | None:
209
+ return None
210
+
211
+ @property
212
+ def accepts_type(self) -> APIContentType | None:
213
+ return None
214
+
215
+ @property
216
+ def content_type(self) -> APIContentType | None:
217
+ return None
218
+
219
+ @property
220
+ def authorization(self) -> APIAuthorization | None:
221
+ return None
222
+
223
+ @property
224
+ def timeout(self) -> float | None:
225
+ return None
226
+
227
+ def body(self) -> bytes | None:
228
+ return None
229
+
230
+ def build_http_request(self, client: APIClient) -> HTTPRequest:
231
+ """Build a platform HTTP request using the provided client."""
232
+ return client.build_http_request(cast("APIRequest[Any]", self))
233
+
234
+ def send(self, client: APIClient, parsing_context: APIParsingContext | None = None) -> APIResponse[T]:
235
+ """Send the request using the provided client."""
236
+ return client.send(cast("APIRequest[T]", self), parsing_context=parsing_context)
237
+
238
+
239
+ class NetworkInterface(Protocol):
240
+ """Protocol for transport implementations used by APIClient."""
241
+ def send(self, request: HTTPRequest) -> RawResponse:
242
+ """Send an HTTP request and return the raw response."""
243
+ ...
244
+
245
+
246
+ @runtime_checkable
247
+ class APIClientListener(Protocol):
248
+ """Listener for request lifecycle events."""
249
+ def will_send(self, client: APIClient, request: HTTPRequest) -> None:
250
+ """Called before a request is sent."""
251
+ ...
252
+
253
+ def did_send(self, client: APIClient, request: HTTPRequest, response: APIResponse[Any]) -> None:
254
+ """Called when a response is received successfully."""
255
+ ...
256
+
257
+ def did_send_error(self, client: APIClient, request: HTTPRequest, error: Exception) -> None:
258
+ """Called when a request fails with an error."""
259
+ ...
260
+
261
+ def should_retry(self, client: APIClient, request: HTTPRequest, rate_limit: APIRateLimit | None) -> APIRetry:
262
+ """Return a retry policy for the given request."""
263
+ ...
264
+
265
+
266
+ class ListenerCollection(Generic[ListenerT]):
267
+ """Thread-unsafe collection of listeners with add/remove semantics."""
268
+ def __init__(self) -> None:
269
+ self._listeners: list[ListenerT] = []
270
+
271
+ def add(self, listener: ListenerT) -> None:
272
+ """Add a listener if it is not already registered."""
273
+ if listener not in self._listeners:
274
+ self._listeners.append(listener)
275
+
276
+ def remove(self, listener: ListenerT) -> None:
277
+ """Remove a listener if present."""
278
+ if listener in self._listeners:
279
+ self._listeners.remove(listener)
280
+
281
+ def __iter__(self) -> Iterator[ListenerT]:
282
+ """Iterate over registered listeners."""
283
+ return iter(self._listeners)
284
+
285
+
286
+ @dataclass(frozen=True)
287
+ class APIClientConfiguration:
288
+ """Immutable configuration for APIClient defaults."""
289
+ base_url: str
290
+ user_agent: str
291
+ additional_http_headers: Mapping[str, str] | None = None
292
+ request_id_header: str | None = None
293
+ timeout: float | None = None
@@ -0,0 +1,104 @@
1
+ # The Okta software accompanied by this notice is provided pursuant to the following terms:
2
+ # Copyright © 2026-Present, Okta, Inc.
3
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
4
+ # License.
5
+ # You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
6
+ # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
7
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8
+ # See the License for the specific language governing permissions and limitations under the License.
9
+ # coding: utf-8
10
+
11
+ from __future__ import annotations
12
+
13
+ from .claims import HasClaims, IdTokenClaim
14
+ from .client_authorization import (
15
+ ClientAssertionAuthorization,
16
+ ClientAuthorization,
17
+ ClientIdAuthorization,
18
+ ClientSecretAuthorization,
19
+ )
20
+ from .config import (
21
+ ConfigurationError,
22
+ ConfigurationFileNotFoundError,
23
+ ConfigurationParseError,
24
+ InvalidConfigurationError,
25
+ OAuth2ClientConfiguration,
26
+ )
27
+ from .errors import OAuth2Error
28
+ from .jwt_bearer_claims import JWTBearerClaims
29
+ from .jwt_context import JWTUsageContext, JWTValidationContext
30
+ from .jwt_token import JWT, JWTType
31
+ from .models import JWK, JWKS, OpenIdConfiguration, TokenInfo, UserInfo
32
+ from .parameters import OAuth2APIRequestCategory, ProvidesOAuth2Parameters
33
+ from .request_protocols import (
34
+ IDTokenValidatorContext,
35
+ OAuth2APIRequest,
36
+ OAuth2TokenRequest,
37
+ OAuth2TokenRequestDefaults,
38
+ )
39
+ from .requests import (
40
+ IntrospectRequest,
41
+ JWKSRequest,
42
+ OpenIDConfigurationRequest,
43
+ RevokeRequest,
44
+ UserInfoRequest,
45
+ )
46
+ from .utils import NullIDTokenValidatorContext
47
+ from .validation_protocols import TokenHashValidator, TokenValidator
48
+ from .validator_registry import (
49
+ get_access_token_validator,
50
+ get_device_secret_validator,
51
+ get_token_validator,
52
+ set_access_token_validator,
53
+ set_device_secret_validator,
54
+ set_token_validator,
55
+ )
56
+ from .validators.token_hash import DefaultTokenHashValidator
57
+ from .validators.token_validator import DefaultTokenValidator
58
+
59
+ __all__ = [
60
+ "JWK",
61
+ "JWKS",
62
+ "JWT",
63
+ "ClientAssertionAuthorization",
64
+ "ClientAuthorization",
65
+ "ClientIdAuthorization",
66
+ "ClientSecretAuthorization",
67
+ "ConfigurationError",
68
+ "ConfigurationFileNotFoundError",
69
+ "ConfigurationParseError",
70
+ "DefaultTokenHashValidator",
71
+ "DefaultTokenValidator",
72
+ "HasClaims",
73
+ "IDTokenValidatorContext",
74
+ "IdTokenClaim",
75
+ "IntrospectRequest",
76
+ "InvalidConfigurationError",
77
+ "JWKSRequest",
78
+ "JWTBearerClaims",
79
+ "JWTType",
80
+ "JWTUsageContext",
81
+ "JWTValidationContext",
82
+ "NullIDTokenValidatorContext",
83
+ "OAuth2APIRequest",
84
+ "OAuth2APIRequestCategory",
85
+ "OAuth2ClientConfiguration",
86
+ "OAuth2Error",
87
+ "OAuth2TokenRequest",
88
+ "OAuth2TokenRequestDefaults",
89
+ "OpenIDConfigurationRequest",
90
+ "OpenIdConfiguration",
91
+ "ProvidesOAuth2Parameters",
92
+ "RevokeRequest",
93
+ "TokenHashValidator",
94
+ "TokenInfo",
95
+ "TokenValidator",
96
+ "UserInfo",
97
+ "UserInfoRequest",
98
+ "get_access_token_validator",
99
+ "get_device_secret_validator",
100
+ "get_token_validator",
101
+ "set_access_token_validator",
102
+ "set_device_secret_validator",
103
+ "set_token_validator",
104
+ ]
@@ -0,0 +1,44 @@
1
+ # The Okta software accompanied by this notice is provided pursuant to the following terms:
2
+ # Copyright © 2026-Present, Okta, Inc.
3
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
4
+ # License.
5
+ # You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
6
+ # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
7
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8
+ # See the License for the specific language governing permissions and limitations under the License.
9
+ # coding: utf-8
10
+
11
+ from __future__ import annotations
12
+
13
+ from enum import Enum
14
+ from typing import Any, Generic, Protocol, TypeVar, runtime_checkable
15
+
16
+ ClaimEnum = TypeVar("ClaimEnum", bound=Enum, contravariant=True)
17
+
18
+
19
+ @runtime_checkable
20
+ class HasClaims(Protocol, Generic[ClaimEnum]):
21
+ """Protocol for objects exposing typed claim values."""
22
+
23
+ def claim(self, claim: ClaimEnum) -> Any:
24
+ ...
25
+
26
+
27
+ class IdTokenClaim(str, Enum):
28
+ """Standard OpenID Connect ID token claims."""
29
+
30
+ ISSUER = "iss"
31
+ SUBJECT = "sub"
32
+ AUDIENCE = "aud"
33
+ EXPIRATION = "exp"
34
+ ISSUED_AT = "iat"
35
+ AUTH_TIME = "auth_time"
36
+ NONCE = "nonce"
37
+ AT_HASH = "at_hash"
38
+ DS_HASH = "ds_hash"
39
+ ACR = "acr"
40
+ AMR = "amr"
41
+ AZP = "azp"
42
+
43
+ def key(self) -> str:
44
+ return str(self.value)