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.
- {workos-5.37.0 → workos-5.38.1}/PKG-INFO +14 -19
- {workos-5.37.0 → workos-5.38.1}/README.md +1 -1
- workos-5.38.1/pyproject.toml +63 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/_base_client.py +13 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/_client_configuration.py +2 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/async_client.py +11 -2
- {workos-5.37.0 → workos-5.38.1/src}/workos/client.py +11 -2
- {workos-5.37.0 → workos-5.38.1/src}/workos/directory_sync.py +0 -2
- workos-5.38.1/src/workos/pipes.py +93 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/portal.py +1 -2
- {workos-5.37.0 → workos-5.38.1/src}/workos/session.py +24 -1
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/fga/warnings.py +3 -3
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/mfa/authentication_factor.py +4 -5
- workos-5.38.1/src/workos/types/pipes/__init__.py +6 -0
- workos-5.38.1/src/workos/types/pipes/pipes.py +34 -0
- workos-5.38.1/src/workos/types/user_management/password_hash_type.py +5 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/user_management/session.py +1 -3
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/workos_model.py +1 -1
- {workos-5.37.0 → workos-5.38.1/src}/workos/user_management.py +9 -7
- {workos-5.37.0 → workos-5.38.1/src}/workos/utils/request_helper.py +0 -1
- {workos-5.37.0 → workos-5.38.1/src}/workos/vault.py +29 -1
- {workos-5.37.0 → workos-5.38.1/src}/workos/widgets.py +0 -1
- workos-5.37.0/LICENSE +0 -21
- workos-5.37.0/setup.cfg +0 -4
- workos-5.37.0/setup.py +0 -53
- workos-5.37.0/tests/test_api_keys.py +0 -50
- workos-5.37.0/tests/test_async_http_client.py +0 -328
- workos-5.37.0/tests/test_audit_logs.py +0 -290
- workos-5.37.0/tests/test_client.py +0 -128
- workos-5.37.0/tests/test_directory_sync.py +0 -373
- workos-5.37.0/tests/test_events.py +0 -42
- workos-5.37.0/tests/test_fga.py +0 -652
- workos-5.37.0/tests/test_mfa.py +0 -257
- workos-5.37.0/tests/test_organization_domains.py +0 -136
- workos-5.37.0/tests/test_organizations.py +0 -300
- workos-5.37.0/tests/test_passwordless.py +0 -56
- workos-5.37.0/tests/test_portal.py +0 -117
- workos-5.37.0/tests/test_session.py +0 -621
- workos-5.37.0/tests/test_sso.py +0 -402
- workos-5.37.0/tests/test_sync_http_client.py +0 -375
- workos-5.37.0/tests/test_user_management.py +0 -1286
- workos-5.37.0/tests/test_user_management_list_sessions.py +0 -73
- workos-5.37.0/tests/test_user_management_revoke_session.py +0 -26
- workos-5.37.0/tests/test_vault.py +0 -460
- workos-5.37.0/tests/test_webhooks.py +0 -156
- workos-5.37.0/tests/test_widgets.py +0 -25
- workos-5.37.0/workos/__about__.py +0 -23
- workos-5.37.0/workos/types/user_management/password_hash_type.py +0 -4
- workos-5.37.0/workos.egg-info/PKG-INFO +0 -93
- workos-5.37.0/workos.egg-info/SOURCES.txt +0 -163
- workos-5.37.0/workos.egg-info/dependency_links.txt +0 -1
- workos-5.37.0/workos.egg-info/not-zip-safe +0 -1
- workos-5.37.0/workos.egg-info/requires.txt +0 -20
- workos-5.37.0/workos.egg-info/top_level.txt +0 -1
- {workos-5.37.0 → workos-5.38.1/src}/workos/__init__.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/api_keys.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/audit_logs.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/events.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/exceptions.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/fga.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/mfa.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/organization_domains.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/organizations.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/passwordless.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/py.typed +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/sso.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/__init__.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/api_keys/__init__.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/api_keys/api_keys.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/audit_logs/__init__.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/audit_logs/audit_log_event.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/audit_logs/audit_log_event_actor.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/audit_logs/audit_log_event_context.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/audit_logs/audit_log_event_target.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/audit_logs/audit_log_export.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/audit_logs/audit_log_metadata.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/directory_sync/__init__.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/directory_sync/directory.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/directory_sync/directory_group.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/directory_sync/directory_state.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/directory_sync/directory_type.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/directory_sync/directory_user.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/directory_sync/list_filters.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/events/__init__.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/events/authentication_payload.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/events/connection_payload_with_legacy_fields.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/events/directory_group_membership_payload.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/events/directory_group_with_previous_attributes.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/events/directory_payload.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/events/directory_payload_with_legacy_fields.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/events/directory_user_with_previous_attributes.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/events/event.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/events/event_model.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/events/event_type.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/events/list_filters.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/events/organization_domain_verification_failed_payload.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/events/previous_attributes.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/events/session_payload.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/feature_flags/__init__.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/feature_flags/feature_flag.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/feature_flags/list_filters.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/fga/__init__.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/fga/authorization_resource_types.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/fga/authorization_resources.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/fga/check.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/fga/list_filters.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/fga/warrant.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/list_resource.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/metadata.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/mfa/__init__.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/mfa/authentication_challenge.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/mfa/authentication_challenge_verification_response.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/mfa/authentication_factor_totp_and_challenge_response.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/mfa/enroll_authentication_factor_type.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/organization_domains/__init__.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/organization_domains/organization_domain.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/organizations/__init__.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/organizations/domain_data_input.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/organizations/list_filters.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/organizations/organization.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/organizations/organization_common.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/passwordless/__init__.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/passwordless/passwordless_session.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/passwordless/passwordless_session_type.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/portal/__init__.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/portal/portal_link.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/portal/portal_link_intent.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/portal/portal_link_intent_options.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/roles/__init__.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/roles/role.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/sso/__init__.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/sso/connection.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/sso/connection_domain.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/sso/profile.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/sso/sso_provider_type.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/user_management/__init__.py +2 -2
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/user_management/authenticate_with_common.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/user_management/authentication_response.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/user_management/email_verification.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/user_management/impersonator.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/user_management/invitation.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/user_management/list_filters.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/user_management/magic_auth.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/user_management/oauth_tokens.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/user_management/organization_membership.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/user_management/password_reset.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/user_management/screen_hint.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/user_management/user.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/user_management/user_management_provider_type.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/vault/__init__.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/vault/key.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/vault/object.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/webhooks/__init__.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/webhooks/webhook.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/webhooks/webhook_model.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/webhooks/webhook_payload.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/widgets/__init__.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/widgets/widget_scope.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/types/widgets/widget_token_response.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/typing/__init__.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/typing/literals.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/typing/sync_or_async.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/typing/untyped_literal.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/typing/webhooks.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/utils/__init__.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/utils/_base_http_client.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/utils/crypto_provider.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/utils/http_client.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/utils/pagination_order.py +0 -0
- {workos-5.37.0 → workos-5.38.1/src}/workos/webhooks.py +0 -0
|
@@ -1,25 +1,20 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: workos
|
|
3
|
-
Version: 5.
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
|
41
|
+
python -m pip install .
|
|
47
42
|
```
|
|
48
43
|
|
|
49
44
|
## 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
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from typing import Optional
|
|
2
|
-
from
|
|
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=
|
|
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=
|
|
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
|
|
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
|
|
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,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
|
+
]
|
|
@@ -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
|