workos 5.38.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.38.0 → workos-5.38.1}/PKG-INFO +14 -19
- {workos-5.38.0 → workos-5.38.1}/README.md +1 -1
- workos-5.38.1/pyproject.toml +63 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/_base_client.py +8 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/_client_configuration.py +2 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/async_client.py +4 -2
- {workos-5.38.0 → workos-5.38.1/src}/workos/client.py +4 -2
- {workos-5.38.0 → workos-5.38.1/src}/workos/directory_sync.py +0 -2
- {workos-5.38.0 → workos-5.38.1/src}/workos/portal.py +1 -2
- {workos-5.38.0 → workos-5.38.1/src}/workos/session.py +24 -1
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/fga/warnings.py +3 -3
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/mfa/authentication_factor.py +4 -5
- workos-5.38.1/src/workos/types/user_management/password_hash_type.py +5 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/user_management/session.py +1 -3
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/workos_model.py +1 -1
- {workos-5.38.0 → workos-5.38.1/src}/workos/user_management.py +9 -7
- {workos-5.38.0 → workos-5.38.1/src}/workos/utils/request_helper.py +0 -1
- {workos-5.38.0 → workos-5.38.1/src}/workos/vault.py +0 -1
- {workos-5.38.0 → workos-5.38.1/src}/workos/widgets.py +0 -1
- workos-5.38.0/LICENSE +0 -21
- workos-5.38.0/setup.cfg +0 -4
- workos-5.38.0/setup.py +0 -53
- workos-5.38.0/tests/test_api_keys.py +0 -50
- workos-5.38.0/tests/test_async_http_client.py +0 -328
- workos-5.38.0/tests/test_audit_logs.py +0 -290
- workos-5.38.0/tests/test_client.py +0 -128
- workos-5.38.0/tests/test_directory_sync.py +0 -373
- workos-5.38.0/tests/test_events.py +0 -42
- workos-5.38.0/tests/test_fga.py +0 -652
- workos-5.38.0/tests/test_mfa.py +0 -257
- workos-5.38.0/tests/test_organization_domains.py +0 -136
- workos-5.38.0/tests/test_organizations.py +0 -300
- workos-5.38.0/tests/test_passwordless.py +0 -56
- workos-5.38.0/tests/test_pipes.py +0 -167
- workos-5.38.0/tests/test_portal.py +0 -117
- workos-5.38.0/tests/test_session.py +0 -621
- workos-5.38.0/tests/test_sso.py +0 -402
- workos-5.38.0/tests/test_sync_http_client.py +0 -375
- workos-5.38.0/tests/test_user_management.py +0 -1286
- workos-5.38.0/tests/test_user_management_list_sessions.py +0 -73
- workos-5.38.0/tests/test_user_management_revoke_session.py +0 -26
- workos-5.38.0/tests/test_vault.py +0 -488
- workos-5.38.0/tests/test_webhooks.py +0 -156
- workos-5.38.0/tests/test_widgets.py +0 -25
- workos-5.38.0/workos/__about__.py +0 -23
- workos-5.38.0/workos/types/user_management/password_hash_type.py +0 -4
- workos-5.38.0/workos.egg-info/PKG-INFO +0 -93
- workos-5.38.0/workos.egg-info/SOURCES.txt +0 -167
- workos-5.38.0/workos.egg-info/dependency_links.txt +0 -1
- workos-5.38.0/workos.egg-info/not-zip-safe +0 -1
- workos-5.38.0/workos.egg-info/requires.txt +0 -20
- workos-5.38.0/workos.egg-info/top_level.txt +0 -1
- {workos-5.38.0 → workos-5.38.1/src}/workos/__init__.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/api_keys.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/audit_logs.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/events.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/exceptions.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/fga.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/mfa.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/organization_domains.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/organizations.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/passwordless.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/pipes.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/py.typed +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/sso.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/__init__.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/api_keys/__init__.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/api_keys/api_keys.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/audit_logs/__init__.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/audit_logs/audit_log_event.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/audit_logs/audit_log_event_actor.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/audit_logs/audit_log_event_context.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/audit_logs/audit_log_event_target.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/audit_logs/audit_log_export.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/audit_logs/audit_log_metadata.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/directory_sync/__init__.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/directory_sync/directory.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/directory_sync/directory_group.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/directory_sync/directory_state.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/directory_sync/directory_type.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/directory_sync/directory_user.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/directory_sync/list_filters.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/events/__init__.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/events/authentication_payload.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/events/connection_payload_with_legacy_fields.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/events/directory_group_membership_payload.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/events/directory_group_with_previous_attributes.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/events/directory_payload.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/events/directory_payload_with_legacy_fields.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/events/directory_user_with_previous_attributes.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/events/event.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/events/event_model.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/events/event_type.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/events/list_filters.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/events/organization_domain_verification_failed_payload.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/events/previous_attributes.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/events/session_payload.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/feature_flags/__init__.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/feature_flags/feature_flag.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/feature_flags/list_filters.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/fga/__init__.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/fga/authorization_resource_types.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/fga/authorization_resources.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/fga/check.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/fga/list_filters.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/fga/warrant.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/list_resource.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/metadata.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/mfa/__init__.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/mfa/authentication_challenge.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/mfa/authentication_challenge_verification_response.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/mfa/authentication_factor_totp_and_challenge_response.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/mfa/enroll_authentication_factor_type.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/organization_domains/__init__.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/organization_domains/organization_domain.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/organizations/__init__.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/organizations/domain_data_input.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/organizations/list_filters.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/organizations/organization.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/organizations/organization_common.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/passwordless/__init__.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/passwordless/passwordless_session.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/passwordless/passwordless_session_type.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/pipes/__init__.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/pipes/pipes.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/portal/__init__.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/portal/portal_link.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/portal/portal_link_intent.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/portal/portal_link_intent_options.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/roles/__init__.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/roles/role.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/sso/__init__.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/sso/connection.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/sso/connection_domain.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/sso/profile.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/sso/sso_provider_type.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/user_management/__init__.py +2 -2
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/user_management/authenticate_with_common.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/user_management/authentication_response.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/user_management/email_verification.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/user_management/impersonator.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/user_management/invitation.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/user_management/list_filters.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/user_management/magic_auth.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/user_management/oauth_tokens.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/user_management/organization_membership.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/user_management/password_reset.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/user_management/screen_hint.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/user_management/user.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/user_management/user_management_provider_type.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/vault/__init__.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/vault/key.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/vault/object.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/webhooks/__init__.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/webhooks/webhook.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/webhooks/webhook_model.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/webhooks/webhook_payload.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/widgets/__init__.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/widgets/widget_scope.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/types/widgets/widget_token_response.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/typing/__init__.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/typing/literals.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/typing/sync_or_async.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/typing/untyped_literal.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/typing/webhooks.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/utils/__init__.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/utils/_base_http_client.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/utils/crypto_provider.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/utils/http_client.py +0 -0
- {workos-5.38.0 → workos-5.38.1/src}/workos/utils/pagination_order.py +0 -0
- {workos-5.38.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.38.
|
|
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"
|
|
@@ -27,6 +27,7 @@ class BaseClient(ClientConfiguration):
|
|
|
27
27
|
_base_url: str
|
|
28
28
|
_client_id: str
|
|
29
29
|
_request_timeout: int
|
|
30
|
+
_jwt_leeway: float
|
|
30
31
|
|
|
31
32
|
def __init__(
|
|
32
33
|
self,
|
|
@@ -35,6 +36,7 @@ class BaseClient(ClientConfiguration):
|
|
|
35
36
|
client_id: Optional[str],
|
|
36
37
|
base_url: Optional[str] = None,
|
|
37
38
|
request_timeout: Optional[int] = None,
|
|
39
|
+
jwt_leeway: float = 0,
|
|
38
40
|
) -> None:
|
|
39
41
|
api_key = api_key or os.getenv("WORKOS_API_KEY")
|
|
40
42
|
if api_key is None:
|
|
@@ -66,6 +68,8 @@ class BaseClient(ClientConfiguration):
|
|
|
66
68
|
else int(os.getenv("WORKOS_REQUEST_TIMEOUT", DEFAULT_REQUEST_TIMEOUT))
|
|
67
69
|
)
|
|
68
70
|
|
|
71
|
+
self._jwt_leeway = jwt_leeway
|
|
72
|
+
|
|
69
73
|
@property
|
|
70
74
|
@abstractmethod
|
|
71
75
|
def api_keys(self) -> ApiKeysModule: ...
|
|
@@ -136,3 +140,7 @@ class BaseClient(ClientConfiguration):
|
|
|
136
140
|
@property
|
|
137
141
|
def request_timeout(self) -> int:
|
|
138
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
|
|
@@ -32,18 +32,20 @@ class AsyncClient(BaseClient):
|
|
|
32
32
|
client_id: Optional[str] = None,
|
|
33
33
|
base_url: Optional[str] = None,
|
|
34
34
|
request_timeout: Optional[int] = None,
|
|
35
|
+
jwt_leeway: float = 0,
|
|
35
36
|
):
|
|
36
37
|
super().__init__(
|
|
37
38
|
api_key=api_key,
|
|
38
39
|
client_id=client_id,
|
|
39
40
|
base_url=base_url,
|
|
40
41
|
request_timeout=request_timeout,
|
|
42
|
+
jwt_leeway=jwt_leeway,
|
|
41
43
|
)
|
|
42
44
|
self._http_client = AsyncHTTPClient(
|
|
43
45
|
api_key=self._api_key,
|
|
44
46
|
base_url=self.base_url,
|
|
45
47
|
client_id=self._client_id,
|
|
46
|
-
version=
|
|
48
|
+
version=version("workos"),
|
|
47
49
|
timeout=self.request_timeout,
|
|
48
50
|
)
|
|
49
51
|
|
|
@@ -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
|
|
@@ -32,18 +32,20 @@ class SyncClient(BaseClient):
|
|
|
32
32
|
client_id: Optional[str] = None,
|
|
33
33
|
base_url: Optional[str] = None,
|
|
34
34
|
request_timeout: Optional[int] = None,
|
|
35
|
+
jwt_leeway: float = 0,
|
|
35
36
|
):
|
|
36
37
|
super().__init__(
|
|
37
38
|
api_key=api_key,
|
|
38
39
|
client_id=client_id,
|
|
39
40
|
base_url=base_url,
|
|
40
41
|
request_timeout=request_timeout,
|
|
42
|
+
jwt_leeway=jwt_leeway,
|
|
41
43
|
)
|
|
42
44
|
self._http_client = SyncHTTPClient(
|
|
43
45
|
api_key=self._api_key,
|
|
44
46
|
base_url=self.base_url,
|
|
45
47
|
client_id=self._client_id,
|
|
46
|
-
version=
|
|
48
|
+
version=version("workos"),
|
|
47
49
|
timeout=self.request_timeout,
|
|
48
50
|
)
|
|
49
51
|
|
|
@@ -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,
|
|
@@ -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
|
|
@@ -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
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from typing import Awaitable, Optional, Protocol, Sequence, Type, Union, cast
|
|
2
2
|
from urllib.parse import urlencode
|
|
3
|
+
|
|
3
4
|
from workos._client_configuration import ClientConfiguration
|
|
4
5
|
from workos.session import AsyncSession, Session
|
|
5
6
|
from workos.types.feature_flags import FeatureFlag
|
|
@@ -38,19 +39,20 @@ from workos.types.user_management.authenticate_with_common import (
|
|
|
38
39
|
AuthenticateWithTotpParameters,
|
|
39
40
|
)
|
|
40
41
|
from workos.types.user_management.authentication_response import (
|
|
41
|
-
AuthKitAuthenticationResponse,
|
|
42
42
|
AuthenticationResponseType,
|
|
43
|
+
AuthKitAuthenticationResponse,
|
|
43
44
|
)
|
|
44
45
|
from workos.types.user_management.list_filters import (
|
|
45
46
|
AuthenticationFactorsListFilters,
|
|
46
47
|
InvitationsListFilters,
|
|
47
48
|
OrganizationMembershipsListFilters,
|
|
49
|
+
SessionsListFilters,
|
|
48
50
|
UsersListFilters,
|
|
49
51
|
)
|
|
50
52
|
from workos.types.user_management.password_hash_type import PasswordHashType
|
|
51
53
|
from workos.types.user_management.screen_hint import ScreenHintType
|
|
52
|
-
from workos.types.user_management.session import SessionConfig
|
|
53
54
|
from workos.types.user_management.session import Session as UserManagementSession
|
|
55
|
+
from workos.types.user_management.session import SessionConfig
|
|
54
56
|
from workos.types.user_management.user_management_provider_type import (
|
|
55
57
|
UserManagementProviderType,
|
|
56
58
|
)
|
|
@@ -59,11 +61,11 @@ from workos.utils.http_client import AsyncHTTPClient, SyncHTTPClient
|
|
|
59
61
|
from workos.utils.pagination_order import PaginationOrder
|
|
60
62
|
from workos.utils.request_helper import (
|
|
61
63
|
DEFAULT_LIST_RESPONSE_LIMIT,
|
|
62
|
-
RESPONSE_TYPE_CODE,
|
|
63
|
-
REQUEST_METHOD_POST,
|
|
64
|
-
REQUEST_METHOD_GET,
|
|
65
64
|
REQUEST_METHOD_DELETE,
|
|
65
|
+
REQUEST_METHOD_GET,
|
|
66
|
+
REQUEST_METHOD_POST,
|
|
66
67
|
REQUEST_METHOD_PUT,
|
|
68
|
+
RESPONSE_TYPE_CODE,
|
|
67
69
|
QueryParameters,
|
|
68
70
|
RequestHelper,
|
|
69
71
|
)
|
|
@@ -120,8 +122,6 @@ FeatureFlagsListResource = WorkOSListResource[
|
|
|
120
122
|
FeatureFlag, FeatureFlagListFilters, ListMetadata
|
|
121
123
|
]
|
|
122
124
|
|
|
123
|
-
from workos.types.user_management.list_filters import SessionsListFilters
|
|
124
|
-
|
|
125
125
|
SessionsListResource = WorkOSListResource[
|
|
126
126
|
UserManagementSession, SessionsListFilters, ListMetadata
|
|
127
127
|
]
|
|
@@ -956,6 +956,7 @@ class UserManagement(UserManagementModule):
|
|
|
956
956
|
client_id=self._http_client.client_id,
|
|
957
957
|
session_data=sealed_session,
|
|
958
958
|
cookie_password=cookie_password,
|
|
959
|
+
jwt_leeway=self._client_configuration.jwt_leeway,
|
|
959
960
|
)
|
|
960
961
|
|
|
961
962
|
def get_user(self, user_id: str) -> User:
|
|
@@ -1679,6 +1680,7 @@ class AsyncUserManagement(UserManagementModule):
|
|
|
1679
1680
|
client_id=self._http_client.client_id,
|
|
1680
1681
|
session_data=sealed_session,
|
|
1681
1682
|
cookie_password=cookie_password,
|
|
1683
|
+
jwt_leeway=self._client_configuration.jwt_leeway,
|
|
1682
1684
|
)
|
|
1683
1685
|
|
|
1684
1686
|
async def get_user(self, user_id: str) -> User:
|
|
@@ -9,7 +9,6 @@ from workos.types.list_resource import (
|
|
|
9
9
|
WorkOSListResource,
|
|
10
10
|
)
|
|
11
11
|
from workos.utils.http_client import SyncHTTPClient
|
|
12
|
-
from workos.utils.pagination_order import PaginationOrder
|
|
13
12
|
from workos.utils.request_helper import (
|
|
14
13
|
DEFAULT_LIST_RESPONSE_LIMIT,
|
|
15
14
|
REQUEST_METHOD_DELETE,
|
workos-5.38.0/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2024 WorkOS
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
workos-5.38.0/setup.cfg
DELETED
workos-5.38.0/setup.py
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
from setuptools import setup, find_packages
|
|
3
|
-
|
|
4
|
-
base_dir = os.path.dirname(__file__)
|
|
5
|
-
|
|
6
|
-
with open("README.md", "r") as f:
|
|
7
|
-
long_description = f.read()
|
|
8
|
-
|
|
9
|
-
about = {}
|
|
10
|
-
with open(os.path.join(base_dir, "workos", "__about__.py")) as f:
|
|
11
|
-
exec(f.read(), about)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def read_requirements(filename):
|
|
15
|
-
with open(filename) as f:
|
|
16
|
-
return [line.strip() for line in f if line.strip() and not line.startswith("#")]
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
setup(
|
|
20
|
-
name=about["__package_name__"],
|
|
21
|
-
version=about["__version__"],
|
|
22
|
-
author=about["__author__"],
|
|
23
|
-
author_email=about["__author_email__"],
|
|
24
|
-
url=about["__package_url__"],
|
|
25
|
-
description=about["__description__"],
|
|
26
|
-
long_description=long_description,
|
|
27
|
-
long_description_content_type="text/markdown",
|
|
28
|
-
package_data={"workos": ["py.typed"]},
|
|
29
|
-
packages=find_packages(
|
|
30
|
-
exclude=[
|
|
31
|
-
"tests*",
|
|
32
|
-
]
|
|
33
|
-
),
|
|
34
|
-
zip_safe=False,
|
|
35
|
-
license=about["__license__"],
|
|
36
|
-
install_requires=read_requirements("requirements.txt"),
|
|
37
|
-
extras_require={
|
|
38
|
-
"dev": read_requirements("requirements-dev.txt"),
|
|
39
|
-
},
|
|
40
|
-
classifiers=[
|
|
41
|
-
"Development Status :: 5 - Production/Stable",
|
|
42
|
-
"Intended Audience :: Developers",
|
|
43
|
-
"License :: OSI Approved :: MIT License",
|
|
44
|
-
"Operating System :: OS Independent",
|
|
45
|
-
"Programming Language :: Python",
|
|
46
|
-
"Programming Language :: Python :: 3",
|
|
47
|
-
"Programming Language :: Python :: 3.8",
|
|
48
|
-
"Programming Language :: Python :: 3.9",
|
|
49
|
-
"Programming Language :: Python :: 3.10",
|
|
50
|
-
"Programming Language :: Python :: 3.11",
|
|
51
|
-
"Programming Language :: Python :: 3.12",
|
|
52
|
-
],
|
|
53
|
-
)
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
# type: ignore
|
|
2
|
-
import pytest
|
|
3
|
-
|
|
4
|
-
from tests.utils.fixtures.mock_api_key import MockApiKey
|
|
5
|
-
from tests.utils.syncify import syncify
|
|
6
|
-
from workos.api_keys import API_KEY_VALIDATION_PATH, ApiKeys, AsyncApiKeys
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
@pytest.mark.sync_and_async(ApiKeys, AsyncApiKeys)
|
|
10
|
-
class TestApiKeys:
|
|
11
|
-
@pytest.fixture
|
|
12
|
-
def mock_api_key(self):
|
|
13
|
-
return MockApiKey().dict()
|
|
14
|
-
|
|
15
|
-
@pytest.fixture
|
|
16
|
-
def api_key(self):
|
|
17
|
-
return "sk_my_api_key"
|
|
18
|
-
|
|
19
|
-
def test_validate_api_key_with_valid_key(
|
|
20
|
-
self,
|
|
21
|
-
module_instance,
|
|
22
|
-
api_key,
|
|
23
|
-
mock_api_key,
|
|
24
|
-
capture_and_mock_http_client_request,
|
|
25
|
-
):
|
|
26
|
-
response_body = {"api_key": mock_api_key}
|
|
27
|
-
request_kwargs = capture_and_mock_http_client_request(
|
|
28
|
-
module_instance._http_client, response_body, 200
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
api_key_details = syncify(module_instance.validate_api_key(value=api_key))
|
|
32
|
-
|
|
33
|
-
assert request_kwargs["url"].endswith(API_KEY_VALIDATION_PATH)
|
|
34
|
-
assert request_kwargs["method"] == "post"
|
|
35
|
-
assert api_key_details.id == mock_api_key["id"]
|
|
36
|
-
assert api_key_details.name == mock_api_key["name"]
|
|
37
|
-
assert api_key_details.object == "api_key"
|
|
38
|
-
|
|
39
|
-
def test_validate_api_key_with_invalid_key(
|
|
40
|
-
self,
|
|
41
|
-
module_instance,
|
|
42
|
-
mock_http_client_with_response,
|
|
43
|
-
):
|
|
44
|
-
mock_http_client_with_response(
|
|
45
|
-
module_instance._http_client,
|
|
46
|
-
{"api_key": None},
|
|
47
|
-
200,
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
assert syncify(module_instance.validate_api_key(value="invalid-key")) is None
|