workos 5.37.0__tar.gz → 5.38.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 (170) hide show
  1. {workos-5.37.0 → workos-5.38.1}/PKG-INFO +14 -19
  2. {workos-5.37.0 → workos-5.38.1}/README.md +1 -1
  3. workos-5.38.1/pyproject.toml +63 -0
  4. {workos-5.37.0 → workos-5.38.1/src}/workos/_base_client.py +13 -0
  5. {workos-5.37.0 → workos-5.38.1/src}/workos/_client_configuration.py +2 -0
  6. {workos-5.37.0 → workos-5.38.1/src}/workos/async_client.py +11 -2
  7. {workos-5.37.0 → workos-5.38.1/src}/workos/client.py +11 -2
  8. {workos-5.37.0 → workos-5.38.1/src}/workos/directory_sync.py +0 -2
  9. workos-5.38.1/src/workos/pipes.py +93 -0
  10. {workos-5.37.0 → workos-5.38.1/src}/workos/portal.py +1 -2
  11. {workos-5.37.0 → workos-5.38.1/src}/workos/session.py +24 -1
  12. {workos-5.37.0 → workos-5.38.1/src}/workos/types/fga/warnings.py +3 -3
  13. {workos-5.37.0 → workos-5.38.1/src}/workos/types/mfa/authentication_factor.py +4 -5
  14. workos-5.38.1/src/workos/types/pipes/__init__.py +6 -0
  15. workos-5.38.1/src/workos/types/pipes/pipes.py +34 -0
  16. workos-5.38.1/src/workos/types/user_management/password_hash_type.py +5 -0
  17. {workos-5.37.0 → workos-5.38.1/src}/workos/types/user_management/session.py +1 -3
  18. {workos-5.37.0 → workos-5.38.1/src}/workos/types/workos_model.py +1 -1
  19. {workos-5.37.0 → workos-5.38.1/src}/workos/user_management.py +9 -7
  20. {workos-5.37.0 → workos-5.38.1/src}/workos/utils/request_helper.py +0 -1
  21. {workos-5.37.0 → workos-5.38.1/src}/workos/vault.py +29 -1
  22. {workos-5.37.0 → workos-5.38.1/src}/workos/widgets.py +0 -1
  23. workos-5.37.0/LICENSE +0 -21
  24. workos-5.37.0/setup.cfg +0 -4
  25. workos-5.37.0/setup.py +0 -53
  26. workos-5.37.0/tests/test_api_keys.py +0 -50
  27. workos-5.37.0/tests/test_async_http_client.py +0 -328
  28. workos-5.37.0/tests/test_audit_logs.py +0 -290
  29. workos-5.37.0/tests/test_client.py +0 -128
  30. workos-5.37.0/tests/test_directory_sync.py +0 -373
  31. workos-5.37.0/tests/test_events.py +0 -42
  32. workos-5.37.0/tests/test_fga.py +0 -652
  33. workos-5.37.0/tests/test_mfa.py +0 -257
  34. workos-5.37.0/tests/test_organization_domains.py +0 -136
  35. workos-5.37.0/tests/test_organizations.py +0 -300
  36. workos-5.37.0/tests/test_passwordless.py +0 -56
  37. workos-5.37.0/tests/test_portal.py +0 -117
  38. workos-5.37.0/tests/test_session.py +0 -621
  39. workos-5.37.0/tests/test_sso.py +0 -402
  40. workos-5.37.0/tests/test_sync_http_client.py +0 -375
  41. workos-5.37.0/tests/test_user_management.py +0 -1286
  42. workos-5.37.0/tests/test_user_management_list_sessions.py +0 -73
  43. workos-5.37.0/tests/test_user_management_revoke_session.py +0 -26
  44. workos-5.37.0/tests/test_vault.py +0 -460
  45. workos-5.37.0/tests/test_webhooks.py +0 -156
  46. workos-5.37.0/tests/test_widgets.py +0 -25
  47. workos-5.37.0/workos/__about__.py +0 -23
  48. workos-5.37.0/workos/types/user_management/password_hash_type.py +0 -4
  49. workos-5.37.0/workos.egg-info/PKG-INFO +0 -93
  50. workos-5.37.0/workos.egg-info/SOURCES.txt +0 -163
  51. workos-5.37.0/workos.egg-info/dependency_links.txt +0 -1
  52. workos-5.37.0/workos.egg-info/not-zip-safe +0 -1
  53. workos-5.37.0/workos.egg-info/requires.txt +0 -20
  54. workos-5.37.0/workos.egg-info/top_level.txt +0 -1
  55. {workos-5.37.0 → workos-5.38.1/src}/workos/__init__.py +0 -0
  56. {workos-5.37.0 → workos-5.38.1/src}/workos/api_keys.py +0 -0
  57. {workos-5.37.0 → workos-5.38.1/src}/workos/audit_logs.py +0 -0
  58. {workos-5.37.0 → workos-5.38.1/src}/workos/events.py +0 -0
  59. {workos-5.37.0 → workos-5.38.1/src}/workos/exceptions.py +0 -0
  60. {workos-5.37.0 → workos-5.38.1/src}/workos/fga.py +0 -0
  61. {workos-5.37.0 → workos-5.38.1/src}/workos/mfa.py +0 -0
  62. {workos-5.37.0 → workos-5.38.1/src}/workos/organization_domains.py +0 -0
  63. {workos-5.37.0 → workos-5.38.1/src}/workos/organizations.py +0 -0
  64. {workos-5.37.0 → workos-5.38.1/src}/workos/passwordless.py +0 -0
  65. {workos-5.37.0 → workos-5.38.1/src}/workos/py.typed +0 -0
  66. {workos-5.37.0 → workos-5.38.1/src}/workos/sso.py +0 -0
  67. {workos-5.37.0 → workos-5.38.1/src}/workos/types/__init__.py +0 -0
  68. {workos-5.37.0 → workos-5.38.1/src}/workos/types/api_keys/__init__.py +0 -0
  69. {workos-5.37.0 → workos-5.38.1/src}/workos/types/api_keys/api_keys.py +0 -0
  70. {workos-5.37.0 → workos-5.38.1/src}/workos/types/audit_logs/__init__.py +0 -0
  71. {workos-5.37.0 → workos-5.38.1/src}/workos/types/audit_logs/audit_log_event.py +0 -0
  72. {workos-5.37.0 → workos-5.38.1/src}/workos/types/audit_logs/audit_log_event_actor.py +0 -0
  73. {workos-5.37.0 → workos-5.38.1/src}/workos/types/audit_logs/audit_log_event_context.py +0 -0
  74. {workos-5.37.0 → workos-5.38.1/src}/workos/types/audit_logs/audit_log_event_target.py +0 -0
  75. {workos-5.37.0 → workos-5.38.1/src}/workos/types/audit_logs/audit_log_export.py +0 -0
  76. {workos-5.37.0 → workos-5.38.1/src}/workos/types/audit_logs/audit_log_metadata.py +0 -0
  77. {workos-5.37.0 → workos-5.38.1/src}/workos/types/directory_sync/__init__.py +0 -0
  78. {workos-5.37.0 → workos-5.38.1/src}/workos/types/directory_sync/directory.py +0 -0
  79. {workos-5.37.0 → workos-5.38.1/src}/workos/types/directory_sync/directory_group.py +0 -0
  80. {workos-5.37.0 → workos-5.38.1/src}/workos/types/directory_sync/directory_state.py +0 -0
  81. {workos-5.37.0 → workos-5.38.1/src}/workos/types/directory_sync/directory_type.py +0 -0
  82. {workos-5.37.0 → workos-5.38.1/src}/workos/types/directory_sync/directory_user.py +0 -0
  83. {workos-5.37.0 → workos-5.38.1/src}/workos/types/directory_sync/list_filters.py +0 -0
  84. {workos-5.37.0 → workos-5.38.1/src}/workos/types/events/__init__.py +0 -0
  85. {workos-5.37.0 → workos-5.38.1/src}/workos/types/events/authentication_payload.py +0 -0
  86. {workos-5.37.0 → workos-5.38.1/src}/workos/types/events/connection_payload_with_legacy_fields.py +0 -0
  87. {workos-5.37.0 → workos-5.38.1/src}/workos/types/events/directory_group_membership_payload.py +0 -0
  88. {workos-5.37.0 → workos-5.38.1/src}/workos/types/events/directory_group_with_previous_attributes.py +0 -0
  89. {workos-5.37.0 → workos-5.38.1/src}/workos/types/events/directory_payload.py +0 -0
  90. {workos-5.37.0 → workos-5.38.1/src}/workos/types/events/directory_payload_with_legacy_fields.py +0 -0
  91. {workos-5.37.0 → workos-5.38.1/src}/workos/types/events/directory_user_with_previous_attributes.py +0 -0
  92. {workos-5.37.0 → workos-5.38.1/src}/workos/types/events/event.py +0 -0
  93. {workos-5.37.0 → workos-5.38.1/src}/workos/types/events/event_model.py +0 -0
  94. {workos-5.37.0 → workos-5.38.1/src}/workos/types/events/event_type.py +0 -0
  95. {workos-5.37.0 → workos-5.38.1/src}/workos/types/events/list_filters.py +0 -0
  96. {workos-5.37.0 → workos-5.38.1/src}/workos/types/events/organization_domain_verification_failed_payload.py +0 -0
  97. {workos-5.37.0 → workos-5.38.1/src}/workos/types/events/previous_attributes.py +0 -0
  98. {workos-5.37.0 → workos-5.38.1/src}/workos/types/events/session_payload.py +0 -0
  99. {workos-5.37.0 → workos-5.38.1/src}/workos/types/feature_flags/__init__.py +0 -0
  100. {workos-5.37.0 → workos-5.38.1/src}/workos/types/feature_flags/feature_flag.py +0 -0
  101. {workos-5.37.0 → workos-5.38.1/src}/workos/types/feature_flags/list_filters.py +0 -0
  102. {workos-5.37.0 → workos-5.38.1/src}/workos/types/fga/__init__.py +0 -0
  103. {workos-5.37.0 → workos-5.38.1/src}/workos/types/fga/authorization_resource_types.py +0 -0
  104. {workos-5.37.0 → workos-5.38.1/src}/workos/types/fga/authorization_resources.py +0 -0
  105. {workos-5.37.0 → workos-5.38.1/src}/workos/types/fga/check.py +0 -0
  106. {workos-5.37.0 → workos-5.38.1/src}/workos/types/fga/list_filters.py +0 -0
  107. {workos-5.37.0 → workos-5.38.1/src}/workos/types/fga/warrant.py +0 -0
  108. {workos-5.37.0 → workos-5.38.1/src}/workos/types/list_resource.py +0 -0
  109. {workos-5.37.0 → workos-5.38.1/src}/workos/types/metadata.py +0 -0
  110. {workos-5.37.0 → workos-5.38.1/src}/workos/types/mfa/__init__.py +0 -0
  111. {workos-5.37.0 → workos-5.38.1/src}/workos/types/mfa/authentication_challenge.py +0 -0
  112. {workos-5.37.0 → workos-5.38.1/src}/workos/types/mfa/authentication_challenge_verification_response.py +0 -0
  113. {workos-5.37.0 → workos-5.38.1/src}/workos/types/mfa/authentication_factor_totp_and_challenge_response.py +0 -0
  114. {workos-5.37.0 → workos-5.38.1/src}/workos/types/mfa/enroll_authentication_factor_type.py +0 -0
  115. {workos-5.37.0 → workos-5.38.1/src}/workos/types/organization_domains/__init__.py +0 -0
  116. {workos-5.37.0 → workos-5.38.1/src}/workos/types/organization_domains/organization_domain.py +0 -0
  117. {workos-5.37.0 → workos-5.38.1/src}/workos/types/organizations/__init__.py +0 -0
  118. {workos-5.37.0 → workos-5.38.1/src}/workos/types/organizations/domain_data_input.py +0 -0
  119. {workos-5.37.0 → workos-5.38.1/src}/workos/types/organizations/list_filters.py +0 -0
  120. {workos-5.37.0 → workos-5.38.1/src}/workos/types/organizations/organization.py +0 -0
  121. {workos-5.37.0 → workos-5.38.1/src}/workos/types/organizations/organization_common.py +0 -0
  122. {workos-5.37.0 → workos-5.38.1/src}/workos/types/passwordless/__init__.py +0 -0
  123. {workos-5.37.0 → workos-5.38.1/src}/workos/types/passwordless/passwordless_session.py +0 -0
  124. {workos-5.37.0 → workos-5.38.1/src}/workos/types/passwordless/passwordless_session_type.py +0 -0
  125. {workos-5.37.0 → workos-5.38.1/src}/workos/types/portal/__init__.py +0 -0
  126. {workos-5.37.0 → workos-5.38.1/src}/workos/types/portal/portal_link.py +0 -0
  127. {workos-5.37.0 → workos-5.38.1/src}/workos/types/portal/portal_link_intent.py +0 -0
  128. {workos-5.37.0 → workos-5.38.1/src}/workos/types/portal/portal_link_intent_options.py +0 -0
  129. {workos-5.37.0 → workos-5.38.1/src}/workos/types/roles/__init__.py +0 -0
  130. {workos-5.37.0 → workos-5.38.1/src}/workos/types/roles/role.py +0 -0
  131. {workos-5.37.0 → workos-5.38.1/src}/workos/types/sso/__init__.py +0 -0
  132. {workos-5.37.0 → workos-5.38.1/src}/workos/types/sso/connection.py +0 -0
  133. {workos-5.37.0 → workos-5.38.1/src}/workos/types/sso/connection_domain.py +0 -0
  134. {workos-5.37.0 → workos-5.38.1/src}/workos/types/sso/profile.py +0 -0
  135. {workos-5.37.0 → workos-5.38.1/src}/workos/types/sso/sso_provider_type.py +0 -0
  136. {workos-5.37.0 → workos-5.38.1/src}/workos/types/user_management/__init__.py +2 -2
  137. {workos-5.37.0 → workos-5.38.1/src}/workos/types/user_management/authenticate_with_common.py +0 -0
  138. {workos-5.37.0 → workos-5.38.1/src}/workos/types/user_management/authentication_response.py +0 -0
  139. {workos-5.37.0 → workos-5.38.1/src}/workos/types/user_management/email_verification.py +0 -0
  140. {workos-5.37.0 → workos-5.38.1/src}/workos/types/user_management/impersonator.py +0 -0
  141. {workos-5.37.0 → workos-5.38.1/src}/workos/types/user_management/invitation.py +0 -0
  142. {workos-5.37.0 → workos-5.38.1/src}/workos/types/user_management/list_filters.py +0 -0
  143. {workos-5.37.0 → workos-5.38.1/src}/workos/types/user_management/magic_auth.py +0 -0
  144. {workos-5.37.0 → workos-5.38.1/src}/workos/types/user_management/oauth_tokens.py +0 -0
  145. {workos-5.37.0 → workos-5.38.1/src}/workos/types/user_management/organization_membership.py +0 -0
  146. {workos-5.37.0 → workos-5.38.1/src}/workos/types/user_management/password_reset.py +0 -0
  147. {workos-5.37.0 → workos-5.38.1/src}/workos/types/user_management/screen_hint.py +0 -0
  148. {workos-5.37.0 → workos-5.38.1/src}/workos/types/user_management/user.py +0 -0
  149. {workos-5.37.0 → workos-5.38.1/src}/workos/types/user_management/user_management_provider_type.py +0 -0
  150. {workos-5.37.0 → workos-5.38.1/src}/workos/types/vault/__init__.py +0 -0
  151. {workos-5.37.0 → workos-5.38.1/src}/workos/types/vault/key.py +0 -0
  152. {workos-5.37.0 → workos-5.38.1/src}/workos/types/vault/object.py +0 -0
  153. {workos-5.37.0 → workos-5.38.1/src}/workos/types/webhooks/__init__.py +0 -0
  154. {workos-5.37.0 → workos-5.38.1/src}/workos/types/webhooks/webhook.py +0 -0
  155. {workos-5.37.0 → workos-5.38.1/src}/workos/types/webhooks/webhook_model.py +0 -0
  156. {workos-5.37.0 → workos-5.38.1/src}/workos/types/webhooks/webhook_payload.py +0 -0
  157. {workos-5.37.0 → workos-5.38.1/src}/workos/types/widgets/__init__.py +0 -0
  158. {workos-5.37.0 → workos-5.38.1/src}/workos/types/widgets/widget_scope.py +0 -0
  159. {workos-5.37.0 → workos-5.38.1/src}/workos/types/widgets/widget_token_response.py +0 -0
  160. {workos-5.37.0 → workos-5.38.1/src}/workos/typing/__init__.py +0 -0
  161. {workos-5.37.0 → workos-5.38.1/src}/workos/typing/literals.py +0 -0
  162. {workos-5.37.0 → workos-5.38.1/src}/workos/typing/sync_or_async.py +0 -0
  163. {workos-5.37.0 → workos-5.38.1/src}/workos/typing/untyped_literal.py +0 -0
  164. {workos-5.37.0 → workos-5.38.1/src}/workos/typing/webhooks.py +0 -0
  165. {workos-5.37.0 → workos-5.38.1/src}/workos/utils/__init__.py +0 -0
  166. {workos-5.37.0 → workos-5.38.1/src}/workos/utils/_base_http_client.py +0 -0
  167. {workos-5.37.0 → workos-5.38.1/src}/workos/utils/crypto_provider.py +0 -0
  168. {workos-5.37.0 → workos-5.38.1/src}/workos/utils/http_client.py +0 -0
  169. {workos-5.37.0 → workos-5.38.1/src}/workos/utils/pagination_order.py +0 -0
  170. {workos-5.37.0 → workos-5.38.1/src}/workos/webhooks.py +0 -0
@@ -1,25 +1,20 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: workos
3
- Version: 5.37.0
3
+ Version: 5.38.1
4
4
  Summary: WorkOS Python Client
5
- Home-page: https://github.com/workos-inc/workos-python
6
5
  Author: WorkOS
7
- Author-email: team@workos.com
8
- License: MIT
9
- Classifier: Development Status :: 5 - Production/Stable
10
- Classifier: Intended Audience :: Developers
11
- Classifier: License :: OSI Approved :: MIT License
12
- Classifier: Operating System :: OS Independent
13
- Classifier: Programming Language :: Python
14
- Classifier: Programming Language :: Python :: 3
15
- Classifier: Programming Language :: Python :: 3.8
16
- Classifier: Programming Language :: Python :: 3.9
17
- Classifier: Programming Language :: Python :: 3.10
18
- Classifier: Programming Language :: Python :: 3.11
19
- Classifier: Programming Language :: Python :: 3.12
6
+ Author-email: WorkOS <team@workos.com>
7
+ License-Expression: MIT
8
+ Requires-Dist: cryptography>=44.0.2
9
+ Requires-Dist: httpx~=0.28.1
10
+ Requires-Dist: pydantic>=2.10.4
11
+ Requires-Dist: pyjwt>=2.10.0 ; python_full_version >= '3.9'
12
+ Requires-Dist: pyjwt>=2.9.0,<2.10 ; python_full_version == '3.8.*'
13
+ Requires-Python: >=3.8
14
+ Project-URL: Changelog, https://workos.com/docs/sdks/python
15
+ Project-URL: Documentation, https://workos.com/docs/reference
16
+ Project-URL: Homepage, https://workos.com/docs/sdks/python
20
17
  Description-Content-Type: text/markdown
21
- Provides-Extra: dev
22
- License-File: LICENSE
23
18
 
24
19
  # WorkOS Python Library
25
20
 
@@ -43,7 +38,7 @@ pip install workos
43
38
  To install from source, clone the repo and run the following:
44
39
 
45
40
  ```
46
- python setup.py install
41
+ python -m pip install .
47
42
  ```
48
43
 
49
44
  ## Configuration
@@ -20,7 +20,7 @@ pip install workos
20
20
  To install from source, clone the repo and run the following:
21
21
 
22
22
  ```
23
- python setup.py install
23
+ python -m pip install .
24
24
  ```
25
25
 
26
26
  ## Configuration
@@ -0,0 +1,63 @@
1
+ [project]
2
+ name = "workos"
3
+ version = "5.38.1"
4
+ description = "WorkOS Python Client"
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ authors = [{ name = "WorkOS", email = "team@workos.com" }]
8
+ requires-python = ">=3.8"
9
+
10
+ dependencies = [
11
+ "cryptography>=44.0.2",
12
+ "httpx~=0.28.1",
13
+ "pydantic>=2.10.4",
14
+ "pyjwt>=2.10.0 ; python_full_version >= '3.9'",
15
+ "pyjwt>=2.9.0,<2.10 ; python_full_version == '3.8.*'",
16
+ ]
17
+
18
+ [project.urls]
19
+ Homepage = "https://workos.com/docs/sdks/python"
20
+ Documentation = "https://workos.com/docs/reference"
21
+ Changelog = "https://workos.com/docs/sdks/python"
22
+
23
+ [dependency-groups]
24
+ dev = [
25
+ { include-group = "test" },
26
+ { include-group = "lint" },
27
+ { include-group = "type_check" },
28
+ ]
29
+ test = [
30
+ "pytest==8.3.4",
31
+ "pytest-asyncio==0.23.8",
32
+ "pytest-cov==5.0.0",
33
+ "six==1.17.0",
34
+ ]
35
+ lint = ["ruff==0.14.5"]
36
+ type_check = ["mypy==1.14.1"]
37
+
38
+
39
+ [tool.mypy]
40
+ packages = "workos"
41
+ warn_return_any = true
42
+ warn_unused_configs = true
43
+ warn_unreachable = true
44
+ warn_redundant_casts = true
45
+ warn_no_return = true
46
+ warn_unused_ignores = true
47
+ implicit_reexport = true
48
+ strict_equality = true
49
+ strict = true
50
+
51
+ [tool.ruff.lint.per-file-ignores]
52
+ "*/__init__.py" = ["F401", "F403"]
53
+
54
+ [tool.ruff.lint.mccabe]
55
+ max-complexity = 10
56
+
57
+ [tool.uv.build-backend]
58
+ source-include = ["py.typed"]
59
+ source-exclude = ["tests*"]
60
+
61
+ [build-system]
62
+ requires = ["uv_build>=0.8.15,<0.9.0"]
63
+ build-backend = "uv_build"
@@ -10,6 +10,7 @@ from workos.events import EventsModule
10
10
  from workos.fga import FGAModule
11
11
  from workos.mfa import MFAModule
12
12
  from workos.organization_domains import OrganizationDomainsModule
13
+ from workos.pipes import PipesModule
13
14
  from workos.organizations import OrganizationsModule
14
15
  from workos.passwordless import PasswordlessModule
15
16
  from workos.portal import PortalModule
@@ -26,6 +27,7 @@ class BaseClient(ClientConfiguration):
26
27
  _base_url: str
27
28
  _client_id: str
28
29
  _request_timeout: int
30
+ _jwt_leeway: float
29
31
 
30
32
  def __init__(
31
33
  self,
@@ -34,6 +36,7 @@ class BaseClient(ClientConfiguration):
34
36
  client_id: Optional[str],
35
37
  base_url: Optional[str] = None,
36
38
  request_timeout: Optional[int] = None,
39
+ jwt_leeway: float = 0,
37
40
  ) -> None:
38
41
  api_key = api_key or os.getenv("WORKOS_API_KEY")
39
42
  if api_key is None:
@@ -65,6 +68,8 @@ class BaseClient(ClientConfiguration):
65
68
  else int(os.getenv("WORKOS_REQUEST_TIMEOUT", DEFAULT_REQUEST_TIMEOUT))
66
69
  )
67
70
 
71
+ self._jwt_leeway = jwt_leeway
72
+
68
73
  @property
69
74
  @abstractmethod
70
75
  def api_keys(self) -> ApiKeysModule: ...
@@ -101,6 +106,10 @@ class BaseClient(ClientConfiguration):
101
106
  @abstractmethod
102
107
  def passwordless(self) -> PasswordlessModule: ...
103
108
 
109
+ @property
110
+ @abstractmethod
111
+ def pipes(self) -> PipesModule: ...
112
+
104
113
  @property
105
114
  @abstractmethod
106
115
  def portal(self) -> PortalModule: ...
@@ -131,3 +140,7 @@ class BaseClient(ClientConfiguration):
131
140
  @property
132
141
  def request_timeout(self) -> int:
133
142
  return self._request_timeout
143
+
144
+ @property
145
+ def jwt_leeway(self) -> float:
146
+ return self._jwt_leeway
@@ -8,3 +8,5 @@ class ClientConfiguration(Protocol):
8
8
  def client_id(self) -> str: ...
9
9
  @property
10
10
  def request_timeout(self) -> int: ...
11
+ @property
12
+ def jwt_leeway(self) -> float: ...
@@ -1,5 +1,5 @@
1
1
  from typing import Optional
2
- from workos.__about__ import __version__
2
+ from importlib.metadata import version
3
3
  from workos._base_client import BaseClient
4
4
  from workos.api_keys import AsyncApiKeys
5
5
  from workos.audit_logs import AuditLogsModule
@@ -10,6 +10,7 @@ from workos.mfa import MFAModule
10
10
  from workos.organizations import AsyncOrganizations
11
11
  from workos.organization_domains import AsyncOrganizationDomains
12
12
  from workos.passwordless import PasswordlessModule
13
+ from workos.pipes import AsyncPipes
13
14
  from workos.portal import PortalModule
14
15
  from workos.sso import AsyncSSO
15
16
  from workos.user_management import AsyncUserManagement
@@ -31,18 +32,20 @@ class AsyncClient(BaseClient):
31
32
  client_id: Optional[str] = None,
32
33
  base_url: Optional[str] = None,
33
34
  request_timeout: Optional[int] = None,
35
+ jwt_leeway: float = 0,
34
36
  ):
35
37
  super().__init__(
36
38
  api_key=api_key,
37
39
  client_id=client_id,
38
40
  base_url=base_url,
39
41
  request_timeout=request_timeout,
42
+ jwt_leeway=jwt_leeway,
40
43
  )
41
44
  self._http_client = AsyncHTTPClient(
42
45
  api_key=self._api_key,
43
46
  base_url=self.base_url,
44
47
  client_id=self._client_id,
45
- version=__version__,
48
+ version=version("workos"),
46
49
  timeout=self.request_timeout,
47
50
  )
48
51
 
@@ -102,6 +105,12 @@ class AsyncClient(BaseClient):
102
105
  "Passwordless APIs are not yet supported in the async client."
103
106
  )
104
107
 
108
+ @property
109
+ def pipes(self) -> AsyncPipes:
110
+ if not getattr(self, "_pipes", None):
111
+ self._pipes = AsyncPipes(self._http_client)
112
+ return self._pipes
113
+
105
114
  @property
106
115
  def portal(self) -> PortalModule:
107
116
  raise NotImplementedError(
@@ -1,5 +1,5 @@
1
+ from importlib.metadata import version
1
2
  from typing import Optional
2
- from workos.__about__ import __version__
3
3
  from workos._base_client import BaseClient
4
4
  from workos.api_keys import ApiKeys
5
5
  from workos.audit_logs import AuditLogs
@@ -8,6 +8,7 @@ from workos.fga import FGA
8
8
  from workos.organizations import Organizations
9
9
  from workos.organization_domains import OrganizationDomains
10
10
  from workos.passwordless import Passwordless
11
+ from workos.pipes import Pipes
11
12
  from workos.portal import Portal
12
13
  from workos.sso import SSO
13
14
  from workos.webhooks import Webhooks
@@ -31,18 +32,20 @@ class SyncClient(BaseClient):
31
32
  client_id: Optional[str] = None,
32
33
  base_url: Optional[str] = None,
33
34
  request_timeout: Optional[int] = None,
35
+ jwt_leeway: float = 0,
34
36
  ):
35
37
  super().__init__(
36
38
  api_key=api_key,
37
39
  client_id=client_id,
38
40
  base_url=base_url,
39
41
  request_timeout=request_timeout,
42
+ jwt_leeway=jwt_leeway,
40
43
  )
41
44
  self._http_client = SyncHTTPClient(
42
45
  api_key=self._api_key,
43
46
  base_url=self.base_url,
44
47
  client_id=self._client_id,
45
- version=__version__,
48
+ version=version("workos"),
46
49
  timeout=self.request_timeout,
47
50
  )
48
51
 
@@ -102,6 +105,12 @@ class SyncClient(BaseClient):
102
105
  self._passwordless = Passwordless(self._http_client)
103
106
  return self._passwordless
104
107
 
108
+ @property
109
+ def pipes(self) -> Pipes:
110
+ if not getattr(self, "_pipes", None):
111
+ self._pipes = Pipes(self._http_client)
112
+ return self._pipes
113
+
105
114
  @property
106
115
  def portal(self) -> Portal:
107
116
  if not getattr(self, "_portal", None):
@@ -176,7 +176,6 @@ class DirectorySync(DirectorySyncModule):
176
176
  after: Optional[str] = None,
177
177
  order: PaginationOrder = "desc",
178
178
  ) -> DirectoryUsersListResource:
179
-
180
179
  list_params: DirectoryUserListFilters = {
181
180
  "limit": limit,
182
181
  "before": before,
@@ -315,7 +314,6 @@ class AsyncDirectorySync(DirectorySyncModule):
315
314
  after: Optional[str] = None,
316
315
  order: PaginationOrder = "desc",
317
316
  ) -> DirectoryUsersListResource:
318
-
319
317
  list_params: DirectoryUserListFilters = {
320
318
  "limit": limit,
321
319
  "before": before,
@@ -0,0 +1,93 @@
1
+ from typing import Dict, Optional, Protocol
2
+
3
+ from workos.types.pipes import (
4
+ GetAccessTokenFailureResponse,
5
+ GetAccessTokenResponse,
6
+ GetAccessTokenSuccessResponse,
7
+ )
8
+ from workos.typing.sync_or_async import SyncOrAsync
9
+ from workos.utils.http_client import AsyncHTTPClient, SyncHTTPClient
10
+ from workos.utils.request_helper import REQUEST_METHOD_POST
11
+
12
+
13
+ class PipesModule(Protocol):
14
+ """Protocol defining the Pipes module interface."""
15
+
16
+ def get_access_token(
17
+ self,
18
+ *,
19
+ provider: str,
20
+ user_id: str,
21
+ organization_id: Optional[str] = None,
22
+ ) -> SyncOrAsync[GetAccessTokenResponse]:
23
+ """Retrieve an access token for a third-party provider.
24
+
25
+ Kwargs:
26
+ provider (str): The third-party provider identifier
27
+ user_id (str): The WorkOS user ID
28
+ organization_id (str, optional): The WorkOS organization ID
29
+
30
+ Returns:
31
+ GetAccessTokenResponse: Success response with token or failure response with error
32
+ """
33
+ ...
34
+
35
+
36
+ class Pipes(PipesModule):
37
+ """Sync implementation of the Pipes module."""
38
+
39
+ _http_client: SyncHTTPClient
40
+
41
+ def __init__(self, http_client: SyncHTTPClient):
42
+ self._http_client = http_client
43
+
44
+ def get_access_token(
45
+ self,
46
+ *,
47
+ provider: str,
48
+ user_id: str,
49
+ organization_id: Optional[str] = None,
50
+ ) -> GetAccessTokenResponse:
51
+ json_data: Dict[str, str] = {"user_id": user_id}
52
+ if organization_id is not None:
53
+ json_data["organization_id"] = organization_id
54
+
55
+ response = self._http_client.request(
56
+ f"data-integrations/{provider}/token",
57
+ method=REQUEST_METHOD_POST,
58
+ json=json_data,
59
+ )
60
+
61
+ if response.get("active") is True:
62
+ return GetAccessTokenSuccessResponse.model_validate(response)
63
+ return GetAccessTokenFailureResponse.model_validate(response)
64
+
65
+
66
+ class AsyncPipes(PipesModule):
67
+ """Async implementation of the Pipes module."""
68
+
69
+ _http_client: AsyncHTTPClient
70
+
71
+ def __init__(self, http_client: AsyncHTTPClient):
72
+ self._http_client = http_client
73
+
74
+ async def get_access_token(
75
+ self,
76
+ *,
77
+ provider: str,
78
+ user_id: str,
79
+ organization_id: Optional[str] = None,
80
+ ) -> GetAccessTokenResponse:
81
+ json_data: Dict[str, str] = {"user_id": user_id}
82
+ if organization_id is not None:
83
+ json_data["organization_id"] = organization_id
84
+
85
+ response = await self._http_client.request(
86
+ f"data-integrations/{provider}/token",
87
+ method=REQUEST_METHOD_POST,
88
+ json=json_data,
89
+ )
90
+
91
+ if response.get("active") is True:
92
+ return GetAccessTokenSuccessResponse.model_validate(response)
93
+ return GetAccessTokenFailureResponse.model_validate(response)
@@ -1,4 +1,4 @@
1
- from typing import Optional, Protocol, Dict, Literal, Union
1
+ from typing import Optional, Protocol
2
2
  from workos.types.portal.portal_link import PortalLink
3
3
  from workos.types.portal.portal_link_intent import PortalLinkIntent
4
4
  from workos.types.portal.portal_link_intent_options import IntentOptions
@@ -36,7 +36,6 @@ class PortalModule(Protocol):
36
36
 
37
37
 
38
38
  class Portal(PortalModule):
39
-
40
39
  _http_client: SyncHTTPClient
41
40
 
42
41
  def __init__(self, http_client: SyncHTTPClient):
@@ -35,6 +35,7 @@ class SessionModule(Protocol):
35
35
  cookie_password: str
36
36
  jwks: PyJWKClient
37
37
  jwk_algorithms: List[str]
38
+ jwt_leeway: float
38
39
 
39
40
  def __init__(
40
41
  self,
@@ -43,6 +44,7 @@ class SessionModule(Protocol):
43
44
  client_id: str,
44
45
  session_data: str,
45
46
  cookie_password: str,
47
+ jwt_leeway: float = 0,
46
48
  ) -> None:
47
49
  # If the cookie password is not provided, throw an error
48
50
  if cookie_password is None or cookie_password == "":
@@ -52,6 +54,7 @@ class SessionModule(Protocol):
52
54
  self.client_id = client_id
53
55
  self.session_data = session_data
54
56
  self.cookie_password = cookie_password
57
+ self.jwt_leeway = jwt_leeway
55
58
 
56
59
  self.jwks = _get_jwks_client(self.user_management.get_jwks_url())
57
60
 
@@ -91,13 +94,13 @@ class SessionModule(Protocol):
91
94
  signing_key.key,
92
95
  algorithms=self.jwk_algorithms,
93
96
  options={"verify_aud": False},
97
+ leeway=self.jwt_leeway,
94
98
  )
95
99
  except jwt.exceptions.InvalidTokenError:
96
100
  return AuthenticateWithSessionCookieErrorResponse(
97
101
  authenticated=False,
98
102
  reason=AuthenticateWithSessionCookieFailureReason.INVALID_JWT,
99
103
  )
100
-
101
104
  return AuthenticateWithSessionCookieSuccessResponse(
102
105
  authenticated=True,
103
106
  session_id=decoded["sid"],
@@ -137,6 +140,20 @@ class SessionModule(Protocol):
137
140
  )
138
141
  return str(result)
139
142
 
143
+ def _is_valid_jwt(self, token: str) -> bool:
144
+ try:
145
+ signing_key = self.jwks.get_signing_key_from_jwt(token)
146
+ jwt.decode(
147
+ token,
148
+ signing_key.key,
149
+ algorithms=self.jwk_algorithms,
150
+ options={"verify_aud": False},
151
+ leeway=self.jwt_leeway,
152
+ )
153
+ return True
154
+ except jwt.exceptions.InvalidTokenError:
155
+ return False
156
+
140
157
  @staticmethod
141
158
  def seal_data(data: Dict[str, Any], key: str) -> str:
142
159
  fernet = Fernet(key)
@@ -163,6 +180,7 @@ class Session(SessionModule):
163
180
  client_id: str,
164
181
  session_data: str,
165
182
  cookie_password: str,
183
+ jwt_leeway: float = 0,
166
184
  ) -> None:
167
185
  # If the cookie password is not provided, throw an error
168
186
  if cookie_password is None or cookie_password == "":
@@ -172,6 +190,7 @@ class Session(SessionModule):
172
190
  self.client_id = client_id
173
191
  self.session_data = session_data
174
192
  self.cookie_password = cookie_password
193
+ self.jwt_leeway = jwt_leeway
175
194
 
176
195
  self.jwks = _get_jwks_client(self.user_management.get_jwks_url())
177
196
 
@@ -224,6 +243,7 @@ class Session(SessionModule):
224
243
  signing_key.key,
225
244
  algorithms=self.jwk_algorithms,
226
245
  options={"verify_aud": False},
246
+ leeway=self.jwt_leeway,
227
247
  )
228
248
 
229
249
  return RefreshWithSessionCookieSuccessResponse(
@@ -255,6 +275,7 @@ class AsyncSession(SessionModule):
255
275
  client_id: str,
256
276
  session_data: str,
257
277
  cookie_password: str,
278
+ jwt_leeway: float = 0,
258
279
  ) -> None:
259
280
  # If the cookie password is not provided, throw an error
260
281
  if cookie_password is None or cookie_password == "":
@@ -264,6 +285,7 @@ class AsyncSession(SessionModule):
264
285
  self.client_id = client_id
265
286
  self.session_data = session_data
266
287
  self.cookie_password = cookie_password
288
+ self.jwt_leeway = jwt_leeway
267
289
 
268
290
  self.jwks = _get_jwks_client(self.user_management.get_jwks_url())
269
291
 
@@ -316,6 +338,7 @@ class AsyncSession(SessionModule):
316
338
  signing_key.key,
317
339
  algorithms=self.jwk_algorithms,
318
340
  options={"verify_aud": False},
341
+ leeway=self.jwt_leeway,
319
342
  )
320
343
 
321
344
  return RefreshWithSessionCookieSuccessResponse(
@@ -1,8 +1,8 @@
1
- from typing import Sequence, Union, Any, Dict, Literal
2
- from typing_extensions import Annotated
1
+ from typing import Any, Dict, Literal, Sequence, Union
3
2
 
4
3
  from pydantic import BeforeValidator
5
4
  from pydantic_core.core_schema import ValidationInfo
5
+ from typing_extensions import Annotated
6
6
 
7
7
  from workos.types.workos_model import WorkOSModel
8
8
 
@@ -12,7 +12,7 @@ class FGABaseWarning(WorkOSModel):
12
12
  message: str
13
13
 
14
14
 
15
- class MissingContextKeysWarning(FGABaseWarning):
15
+ class MissingContextKeysWarning(FGABaseWarning): # type: ignore[override, unused-ignore]
16
16
  code: Literal["missing_context_keys"]
17
17
  keys: Sequence[str]
18
18
 
@@ -1,13 +1,12 @@
1
1
  from typing import Literal, Optional, Union
2
2
 
3
- from workos.types.workos_model import WorkOSModel
4
3
  from workos.types.mfa.enroll_authentication_factor_type import (
5
4
  SmsAuthenticationFactorType,
6
5
  TotpAuthenticationFactorType,
7
6
  )
7
+ from workos.types.workos_model import WorkOSModel
8
8
  from workos.typing.literals import LiteralOrUntyped
9
9
 
10
-
11
10
  AuthenticationFactorType = Literal[
12
11
  "generic_otp", SmsAuthenticationFactorType, TotpAuthenticationFactorType
13
12
  ]
@@ -43,21 +42,21 @@ class AuthenticationFactorBase(WorkOSModel):
43
42
  user_id: Optional[str] = None
44
43
 
45
44
 
46
- class AuthenticationFactorTotp(AuthenticationFactorBase):
45
+ class AuthenticationFactorTotp(AuthenticationFactorBase): # type: ignore[override, unused-ignore]
47
46
  """Representation of a MFA Authentication Factor Response as returned by WorkOS through the MFA feature."""
48
47
 
49
48
  type: TotpAuthenticationFactorType
50
49
  totp: TotpFactor
51
50
 
52
51
 
53
- class AuthenticationFactorTotpExtended(AuthenticationFactorBase):
52
+ class AuthenticationFactorTotpExtended(AuthenticationFactorBase): # type: ignore[override, unused-ignore]
54
53
  """Representation of a MFA Authentication Factor Response when enrolling an authentication factor."""
55
54
 
56
55
  type: TotpAuthenticationFactorType
57
56
  totp: ExtendedTotpFactor
58
57
 
59
58
 
60
- class AuthenticationFactorSms(AuthenticationFactorBase):
59
+ class AuthenticationFactorSms(AuthenticationFactorBase): # type: ignore[override, unused-ignore]
61
60
  """Representation of a SMS Authentication Factor Response as returned by WorkOS through the MFA feature."""
62
61
 
63
62
  type: SmsAuthenticationFactorType
@@ -0,0 +1,6 @@
1
+ from workos.types.pipes.pipes import (
2
+ AccessToken as AccessToken,
3
+ GetAccessTokenFailureResponse as GetAccessTokenFailureResponse,
4
+ GetAccessTokenResponse as GetAccessTokenResponse,
5
+ GetAccessTokenSuccessResponse as GetAccessTokenSuccessResponse,
6
+ )
@@ -0,0 +1,34 @@
1
+ from datetime import datetime
2
+ from typing import Literal, Optional, Sequence, Union
3
+
4
+ from workos.types.workos_model import WorkOSModel
5
+
6
+
7
+ class AccessToken(WorkOSModel):
8
+ """Represents an OAuth access token for a third-party provider."""
9
+
10
+ object: Literal["access_token"]
11
+ access_token: str
12
+ expires_at: Optional[datetime] = None
13
+ scopes: Sequence[str]
14
+ missing_scopes: Sequence[str]
15
+
16
+
17
+ class GetAccessTokenSuccessResponse(WorkOSModel):
18
+ """Successful response containing the access token."""
19
+
20
+ active: Literal[True]
21
+ access_token: AccessToken
22
+
23
+
24
+ class GetAccessTokenFailureResponse(WorkOSModel):
25
+ """Failed response indicating why the token couldn't be retrieved."""
26
+
27
+ active: Literal[False]
28
+ error: Literal["not_installed", "needs_reauthorization"]
29
+
30
+
31
+ GetAccessTokenResponse = Union[
32
+ GetAccessTokenSuccessResponse,
33
+ GetAccessTokenFailureResponse,
34
+ ]
@@ -0,0 +1,5 @@
1
+ from typing import Literal
2
+
3
+ PasswordHashType = Literal[
4
+ "bcrypt", "firebase-scrypt", "pbkdf2", "scrypt", "ssha", "argon2"
5
+ ]
@@ -1,7 +1,5 @@
1
1
  from enum import Enum
2
- from typing import Optional, Sequence, TypedDict, Union
3
-
4
- from typing_extensions import Literal
2
+ from typing import Literal, Optional, Sequence, TypedDict, Union
5
3
 
6
4
  from workos.types.user_management.impersonator import Impersonator
7
5
  from workos.types.user_management.user import User
@@ -14,7 +14,7 @@ class WorkOSModel(BaseModel):
14
14
  by_alias: bool = False,
15
15
  exclude_unset: bool = False,
16
16
  exclude_defaults: bool = False,
17
- exclude_none: bool = False
17
+ exclude_none: bool = False,
18
18
  ) -> Dict[str, Any]:
19
19
  return self.model_dump(
20
20
  include=include,