workos 5.26.0__tar.gz → 5.26.1__tar.gz

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 (156) hide show
  1. {workos-5.26.0 → workos-5.26.1}/PKG-INFO +1 -1
  2. {workos-5.26.0 → workos-5.26.1}/tests/test_session.py +48 -1
  3. {workos-5.26.0 → workos-5.26.1}/workos/__about__.py +1 -1
  4. {workos-5.26.0 → workos-5.26.1}/workos/session.py +9 -3
  5. {workos-5.26.0 → workos-5.26.1}/workos.egg-info/PKG-INFO +1 -1
  6. {workos-5.26.0 → workos-5.26.1}/LICENSE +0 -0
  7. {workos-5.26.0 → workos-5.26.1}/README.md +0 -0
  8. {workos-5.26.0 → workos-5.26.1}/setup.cfg +0 -0
  9. {workos-5.26.0 → workos-5.26.1}/setup.py +0 -0
  10. {workos-5.26.0 → workos-5.26.1}/tests/test_async_http_client.py +0 -0
  11. {workos-5.26.0 → workos-5.26.1}/tests/test_audit_logs.py +0 -0
  12. {workos-5.26.0 → workos-5.26.1}/tests/test_client.py +0 -0
  13. {workos-5.26.0 → workos-5.26.1}/tests/test_directory_sync.py +0 -0
  14. {workos-5.26.0 → workos-5.26.1}/tests/test_events.py +0 -0
  15. {workos-5.26.0 → workos-5.26.1}/tests/test_fga.py +0 -0
  16. {workos-5.26.0 → workos-5.26.1}/tests/test_mfa.py +0 -0
  17. {workos-5.26.0 → workos-5.26.1}/tests/test_organization_domains.py +0 -0
  18. {workos-5.26.0 → workos-5.26.1}/tests/test_organizations.py +0 -0
  19. {workos-5.26.0 → workos-5.26.1}/tests/test_passwordless.py +0 -0
  20. {workos-5.26.0 → workos-5.26.1}/tests/test_portal.py +0 -0
  21. {workos-5.26.0 → workos-5.26.1}/tests/test_sso.py +0 -0
  22. {workos-5.26.0 → workos-5.26.1}/tests/test_sync_http_client.py +0 -0
  23. {workos-5.26.0 → workos-5.26.1}/tests/test_user_management.py +0 -0
  24. {workos-5.26.0 → workos-5.26.1}/tests/test_vault.py +0 -0
  25. {workos-5.26.0 → workos-5.26.1}/tests/test_webhooks.py +0 -0
  26. {workos-5.26.0 → workos-5.26.1}/tests/test_widgets.py +0 -0
  27. {workos-5.26.0 → workos-5.26.1}/workos/__init__.py +0 -0
  28. {workos-5.26.0 → workos-5.26.1}/workos/_base_client.py +0 -0
  29. {workos-5.26.0 → workos-5.26.1}/workos/_client_configuration.py +0 -0
  30. {workos-5.26.0 → workos-5.26.1}/workos/async_client.py +0 -0
  31. {workos-5.26.0 → workos-5.26.1}/workos/audit_logs.py +0 -0
  32. {workos-5.26.0 → workos-5.26.1}/workos/client.py +0 -0
  33. {workos-5.26.0 → workos-5.26.1}/workos/directory_sync.py +0 -0
  34. {workos-5.26.0 → workos-5.26.1}/workos/events.py +0 -0
  35. {workos-5.26.0 → workos-5.26.1}/workos/exceptions.py +0 -0
  36. {workos-5.26.0 → workos-5.26.1}/workos/fga.py +0 -0
  37. {workos-5.26.0 → workos-5.26.1}/workos/mfa.py +0 -0
  38. {workos-5.26.0 → workos-5.26.1}/workos/organization_domains.py +0 -0
  39. {workos-5.26.0 → workos-5.26.1}/workos/organizations.py +0 -0
  40. {workos-5.26.0 → workos-5.26.1}/workos/passwordless.py +0 -0
  41. {workos-5.26.0 → workos-5.26.1}/workos/portal.py +0 -0
  42. {workos-5.26.0 → workos-5.26.1}/workos/py.typed +0 -0
  43. {workos-5.26.0 → workos-5.26.1}/workos/sso.py +0 -0
  44. {workos-5.26.0 → workos-5.26.1}/workos/types/__init__.py +0 -0
  45. {workos-5.26.0 → workos-5.26.1}/workos/types/audit_logs/__init__.py +0 -0
  46. {workos-5.26.0 → workos-5.26.1}/workos/types/audit_logs/audit_log_event.py +0 -0
  47. {workos-5.26.0 → workos-5.26.1}/workos/types/audit_logs/audit_log_event_actor.py +0 -0
  48. {workos-5.26.0 → workos-5.26.1}/workos/types/audit_logs/audit_log_event_context.py +0 -0
  49. {workos-5.26.0 → workos-5.26.1}/workos/types/audit_logs/audit_log_event_target.py +0 -0
  50. {workos-5.26.0 → workos-5.26.1}/workos/types/audit_logs/audit_log_export.py +0 -0
  51. {workos-5.26.0 → workos-5.26.1}/workos/types/audit_logs/audit_log_metadata.py +0 -0
  52. {workos-5.26.0 → workos-5.26.1}/workos/types/directory_sync/__init__.py +0 -0
  53. {workos-5.26.0 → workos-5.26.1}/workos/types/directory_sync/directory.py +0 -0
  54. {workos-5.26.0 → workos-5.26.1}/workos/types/directory_sync/directory_group.py +0 -0
  55. {workos-5.26.0 → workos-5.26.1}/workos/types/directory_sync/directory_state.py +0 -0
  56. {workos-5.26.0 → workos-5.26.1}/workos/types/directory_sync/directory_type.py +0 -0
  57. {workos-5.26.0 → workos-5.26.1}/workos/types/directory_sync/directory_user.py +0 -0
  58. {workos-5.26.0 → workos-5.26.1}/workos/types/directory_sync/list_filters.py +0 -0
  59. {workos-5.26.0 → workos-5.26.1}/workos/types/events/__init__.py +0 -0
  60. {workos-5.26.0 → workos-5.26.1}/workos/types/events/authentication_payload.py +0 -0
  61. {workos-5.26.0 → workos-5.26.1}/workos/types/events/connection_payload_with_legacy_fields.py +0 -0
  62. {workos-5.26.0 → workos-5.26.1}/workos/types/events/directory_group_membership_payload.py +0 -0
  63. {workos-5.26.0 → workos-5.26.1}/workos/types/events/directory_group_with_previous_attributes.py +0 -0
  64. {workos-5.26.0 → workos-5.26.1}/workos/types/events/directory_payload.py +0 -0
  65. {workos-5.26.0 → workos-5.26.1}/workos/types/events/directory_payload_with_legacy_fields.py +0 -0
  66. {workos-5.26.0 → workos-5.26.1}/workos/types/events/directory_user_with_previous_attributes.py +0 -0
  67. {workos-5.26.0 → workos-5.26.1}/workos/types/events/event.py +0 -0
  68. {workos-5.26.0 → workos-5.26.1}/workos/types/events/event_model.py +0 -0
  69. {workos-5.26.0 → workos-5.26.1}/workos/types/events/event_type.py +0 -0
  70. {workos-5.26.0 → workos-5.26.1}/workos/types/events/list_filters.py +0 -0
  71. {workos-5.26.0 → workos-5.26.1}/workos/types/events/organization_domain_verification_failed_payload.py +0 -0
  72. {workos-5.26.0 → workos-5.26.1}/workos/types/events/previous_attributes.py +0 -0
  73. {workos-5.26.0 → workos-5.26.1}/workos/types/events/session_created_payload.py +0 -0
  74. {workos-5.26.0 → workos-5.26.1}/workos/types/fga/__init__.py +0 -0
  75. {workos-5.26.0 → workos-5.26.1}/workos/types/fga/authorization_resource_types.py +0 -0
  76. {workos-5.26.0 → workos-5.26.1}/workos/types/fga/authorization_resources.py +0 -0
  77. {workos-5.26.0 → workos-5.26.1}/workos/types/fga/check.py +0 -0
  78. {workos-5.26.0 → workos-5.26.1}/workos/types/fga/list_filters.py +0 -0
  79. {workos-5.26.0 → workos-5.26.1}/workos/types/fga/warnings.py +0 -0
  80. {workos-5.26.0 → workos-5.26.1}/workos/types/fga/warrant.py +0 -0
  81. {workos-5.26.0 → workos-5.26.1}/workos/types/list_resource.py +0 -0
  82. {workos-5.26.0 → workos-5.26.1}/workos/types/metadata.py +0 -0
  83. {workos-5.26.0 → workos-5.26.1}/workos/types/mfa/__init__.py +0 -0
  84. {workos-5.26.0 → workos-5.26.1}/workos/types/mfa/authentication_challenge.py +0 -0
  85. {workos-5.26.0 → workos-5.26.1}/workos/types/mfa/authentication_challenge_verification_response.py +0 -0
  86. {workos-5.26.0 → workos-5.26.1}/workos/types/mfa/authentication_factor.py +0 -0
  87. {workos-5.26.0 → workos-5.26.1}/workos/types/mfa/authentication_factor_totp_and_challenge_response.py +0 -0
  88. {workos-5.26.0 → workos-5.26.1}/workos/types/mfa/enroll_authentication_factor_type.py +0 -0
  89. {workos-5.26.0 → workos-5.26.1}/workos/types/organization_domains/__init__.py +0 -0
  90. {workos-5.26.0 → workos-5.26.1}/workos/types/organization_domains/organization_domain.py +0 -0
  91. {workos-5.26.0 → workos-5.26.1}/workos/types/organizations/__init__.py +0 -0
  92. {workos-5.26.0 → workos-5.26.1}/workos/types/organizations/domain_data_input.py +0 -0
  93. {workos-5.26.0 → workos-5.26.1}/workos/types/organizations/list_filters.py +0 -0
  94. {workos-5.26.0 → workos-5.26.1}/workos/types/organizations/organization.py +0 -0
  95. {workos-5.26.0 → workos-5.26.1}/workos/types/organizations/organization_common.py +0 -0
  96. {workos-5.26.0 → workos-5.26.1}/workos/types/passwordless/__init__.py +0 -0
  97. {workos-5.26.0 → workos-5.26.1}/workos/types/passwordless/passwordless_session.py +0 -0
  98. {workos-5.26.0 → workos-5.26.1}/workos/types/passwordless/passwordless_session_type.py +0 -0
  99. {workos-5.26.0 → workos-5.26.1}/workos/types/portal/__init__.py +0 -0
  100. {workos-5.26.0 → workos-5.26.1}/workos/types/portal/portal_link.py +0 -0
  101. {workos-5.26.0 → workos-5.26.1}/workos/types/portal/portal_link_intent.py +0 -0
  102. {workos-5.26.0 → workos-5.26.1}/workos/types/portal/portal_link_intent_options.py +0 -0
  103. {workos-5.26.0 → workos-5.26.1}/workos/types/roles/__init__.py +0 -0
  104. {workos-5.26.0 → workos-5.26.1}/workos/types/roles/role.py +0 -0
  105. {workos-5.26.0 → workos-5.26.1}/workos/types/sso/__init__.py +0 -0
  106. {workos-5.26.0 → workos-5.26.1}/workos/types/sso/connection.py +0 -0
  107. {workos-5.26.0 → workos-5.26.1}/workos/types/sso/connection_domain.py +0 -0
  108. {workos-5.26.0 → workos-5.26.1}/workos/types/sso/profile.py +0 -0
  109. {workos-5.26.0 → workos-5.26.1}/workos/types/sso/sso_provider_type.py +0 -0
  110. {workos-5.26.0 → workos-5.26.1}/workos/types/user_management/__init__.py +0 -0
  111. {workos-5.26.0 → workos-5.26.1}/workos/types/user_management/authenticate_with_common.py +0 -0
  112. {workos-5.26.0 → workos-5.26.1}/workos/types/user_management/authentication_response.py +0 -0
  113. {workos-5.26.0 → workos-5.26.1}/workos/types/user_management/email_verification.py +0 -0
  114. {workos-5.26.0 → workos-5.26.1}/workos/types/user_management/impersonator.py +0 -0
  115. {workos-5.26.0 → workos-5.26.1}/workos/types/user_management/invitation.py +0 -0
  116. {workos-5.26.0 → workos-5.26.1}/workos/types/user_management/list_filters.py +0 -0
  117. {workos-5.26.0 → workos-5.26.1}/workos/types/user_management/magic_auth.py +0 -0
  118. {workos-5.26.0 → workos-5.26.1}/workos/types/user_management/oauth_tokens.py +0 -0
  119. {workos-5.26.0 → workos-5.26.1}/workos/types/user_management/organization_membership.py +0 -0
  120. {workos-5.26.0 → workos-5.26.1}/workos/types/user_management/password_hash_type.py +0 -0
  121. {workos-5.26.0 → workos-5.26.1}/workos/types/user_management/password_reset.py +0 -0
  122. {workos-5.26.0 → workos-5.26.1}/workos/types/user_management/screen_hint.py +0 -0
  123. {workos-5.26.0 → workos-5.26.1}/workos/types/user_management/session.py +0 -0
  124. {workos-5.26.0 → workos-5.26.1}/workos/types/user_management/user.py +0 -0
  125. {workos-5.26.0 → workos-5.26.1}/workos/types/user_management/user_management_provider_type.py +0 -0
  126. {workos-5.26.0 → workos-5.26.1}/workos/types/vault/__init__.py +0 -0
  127. {workos-5.26.0 → workos-5.26.1}/workos/types/vault/key.py +0 -0
  128. {workos-5.26.0 → workos-5.26.1}/workos/types/vault/object.py +0 -0
  129. {workos-5.26.0 → workos-5.26.1}/workos/types/webhooks/__init__.py +0 -0
  130. {workos-5.26.0 → workos-5.26.1}/workos/types/webhooks/webhook.py +0 -0
  131. {workos-5.26.0 → workos-5.26.1}/workos/types/webhooks/webhook_model.py +0 -0
  132. {workos-5.26.0 → workos-5.26.1}/workos/types/webhooks/webhook_payload.py +0 -0
  133. {workos-5.26.0 → workos-5.26.1}/workos/types/widgets/__init__.py +0 -0
  134. {workos-5.26.0 → workos-5.26.1}/workos/types/widgets/widget_scope.py +0 -0
  135. {workos-5.26.0 → workos-5.26.1}/workos/types/widgets/widget_token_response.py +0 -0
  136. {workos-5.26.0 → workos-5.26.1}/workos/types/workos_model.py +0 -0
  137. {workos-5.26.0 → workos-5.26.1}/workos/typing/__init__.py +0 -0
  138. {workos-5.26.0 → workos-5.26.1}/workos/typing/literals.py +0 -0
  139. {workos-5.26.0 → workos-5.26.1}/workos/typing/sync_or_async.py +0 -0
  140. {workos-5.26.0 → workos-5.26.1}/workos/typing/untyped_literal.py +0 -0
  141. {workos-5.26.0 → workos-5.26.1}/workos/typing/webhooks.py +0 -0
  142. {workos-5.26.0 → workos-5.26.1}/workos/user_management.py +0 -0
  143. {workos-5.26.0 → workos-5.26.1}/workos/utils/__init__.py +0 -0
  144. {workos-5.26.0 → workos-5.26.1}/workos/utils/_base_http_client.py +0 -0
  145. {workos-5.26.0 → workos-5.26.1}/workos/utils/crypto_provider.py +0 -0
  146. {workos-5.26.0 → workos-5.26.1}/workos/utils/http_client.py +0 -0
  147. {workos-5.26.0 → workos-5.26.1}/workos/utils/pagination_order.py +0 -0
  148. {workos-5.26.0 → workos-5.26.1}/workos/utils/request_helper.py +0 -0
  149. {workos-5.26.0 → workos-5.26.1}/workos/vault.py +0 -0
  150. {workos-5.26.0 → workos-5.26.1}/workos/webhooks.py +0 -0
  151. {workos-5.26.0 → workos-5.26.1}/workos/widgets.py +0 -0
  152. {workos-5.26.0 → workos-5.26.1}/workos.egg-info/SOURCES.txt +0 -0
  153. {workos-5.26.0 → workos-5.26.1}/workos.egg-info/dependency_links.txt +0 -0
  154. {workos-5.26.0 → workos-5.26.1}/workos.egg-info/not-zip-safe +0 -0
  155. {workos-5.26.0 → workos-5.26.1}/workos.egg-info/requires.txt +0 -0
  156. {workos-5.26.0 → workos-5.26.1}/workos.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: workos
3
- Version: 5.26.0
3
+ Version: 5.26.1
4
4
  Summary: WorkOS Python Client
5
5
  Home-page: https://github.com/workos-inc/workos-python
6
6
  Author: WorkOS
@@ -2,9 +2,10 @@ import pytest
2
2
  from unittest.mock import AsyncMock, Mock, patch
3
3
  import jwt
4
4
  from datetime import datetime, timezone
5
+ import concurrent.futures
5
6
 
6
7
  from tests.conftest import with_jwks_mock
7
- from workos.session import AsyncSession, Session
8
+ from workos.session import AsyncSession, Session, _get_jwks_client
8
9
  from workos.types.user_management.authentication_response import (
9
10
  RefreshTokenAuthenticationResponse,
10
11
  )
@@ -20,6 +21,12 @@ from cryptography.hazmat.primitives.asymmetric import rsa
20
21
 
21
22
 
22
23
  class SessionFixtures:
24
+ @pytest.fixture(autouse=True)
25
+ def clear_jwks_cache(self):
26
+ _get_jwks_client.cache_clear()
27
+ yield
28
+ _get_jwks_client.cache_clear()
29
+
23
30
  @pytest.fixture
24
31
  def session_constants(self):
25
32
  # Generate RSA key pair for testing
@@ -491,3 +498,43 @@ class TestAsyncSession(SessionFixtures):
491
498
  response = await session.refresh()
492
499
 
493
500
  assert isinstance(response, RefreshWithSessionCookieSuccessResponse)
501
+
502
+
503
+ class TestJWKSCaching:
504
+ def test_jwks_client_caching_same_url(self):
505
+ url = "https://api.workos.com/sso/jwks/test"
506
+
507
+ client1 = _get_jwks_client(url)
508
+ client2 = _get_jwks_client(url)
509
+
510
+ # Should be the exact same instance
511
+ assert client1 is client2
512
+ assert id(client1) == id(client2)
513
+
514
+ def test_jwks_client_caching_different_urls(self):
515
+ url1 = "https://api.workos.com/sso/jwks/client1"
516
+ url2 = "https://api.workos.com/sso/jwks/client2"
517
+
518
+ client1 = _get_jwks_client(url1)
519
+ client2 = _get_jwks_client(url2)
520
+
521
+ # Should be different instances
522
+ assert client1 is not client2
523
+ assert id(client1) != id(client2)
524
+
525
+ def test_jwks_cache_thread_safety(self):
526
+ url = "https://api.workos.com/sso/jwks/thread_test"
527
+ clients = []
528
+
529
+ def get_client():
530
+ return _get_jwks_client(url)
531
+
532
+ with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
533
+ futures = [executor.submit(get_client) for _ in range(10)]
534
+ clients = [future.result() for future in futures]
535
+
536
+ first_client = clients[0]
537
+ for client in clients[1:]:
538
+ assert (
539
+ client is first_client
540
+ ), "All concurrent calls should return the same instance"
@@ -12,7 +12,7 @@ __package_name__ = "workos"
12
12
 
13
13
  __package_url__ = "https://github.com/workos-inc/workos-python"
14
14
 
15
- __version__ = "5.26.0"
15
+ __version__ = "5.26.1"
16
16
 
17
17
  __author__ = "WorkOS"
18
18
 
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
  from typing import TYPE_CHECKING, List, Protocol
3
3
 
4
+ from functools import lru_cache
4
5
  import json
5
6
  from typing import Any, Dict, Optional, Union, cast
6
7
  import jwt
@@ -21,6 +22,11 @@ if TYPE_CHECKING:
21
22
  from workos.user_management import AsyncUserManagement, UserManagement
22
23
 
23
24
 
25
+ @lru_cache(maxsize=None)
26
+ def _get_jwks_client(jwks_url: str) -> PyJWKClient:
27
+ return PyJWKClient(jwks_url)
28
+
29
+
24
30
  class SessionModule(Protocol):
25
31
  user_management: "UserManagementModule"
26
32
  client_id: str
@@ -46,7 +52,7 @@ class SessionModule(Protocol):
46
52
  self.session_data = session_data
47
53
  self.cookie_password = cookie_password
48
54
 
49
- self.jwks = PyJWKClient(self.user_management.get_jwks_url())
55
+ self.jwks = _get_jwks_client(self.user_management.get_jwks_url())
50
56
 
51
57
  # Algorithms are hardcoded for security reasons. See https://pyjwt.readthedocs.io/en/stable/algorithms.html#specifying-an-algorithm
52
58
  self.jwk_algorithms = ["RS256"]
@@ -164,7 +170,7 @@ class Session(SessionModule):
164
170
  self.session_data = session_data
165
171
  self.cookie_password = cookie_password
166
172
 
167
- self.jwks = PyJWKClient(self.user_management.get_jwks_url())
173
+ self.jwks = _get_jwks_client(self.user_management.get_jwks_url())
168
174
 
169
175
  # Algorithms are hardcoded for security reasons. See https://pyjwt.readthedocs.io/en/stable/algorithms.html#specifying-an-algorithm
170
176
  self.jwk_algorithms = ["RS256"]
@@ -254,7 +260,7 @@ class AsyncSession(SessionModule):
254
260
  self.session_data = session_data
255
261
  self.cookie_password = cookie_password
256
262
 
257
- self.jwks = PyJWKClient(self.user_management.get_jwks_url())
263
+ self.jwks = _get_jwks_client(self.user_management.get_jwks_url())
258
264
 
259
265
  # Algorithms are hardcoded for security reasons. See https://pyjwt.readthedocs.io/en/stable/algorithms.html#specifying-an-algorithm
260
266
  self.jwk_algorithms = ["RS256"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: workos
3
- Version: 5.26.0
3
+ Version: 5.26.1
4
4
  Summary: WorkOS Python Client
5
5
  Home-page: https://github.com/workos-inc/workos-python
6
6
  Author: WorkOS
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes