workos 5.20.1__tar.gz → 5.21.0__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 (147) hide show
  1. {workos-5.20.1 → workos-5.21.0}/PKG-INFO +1 -1
  2. {workos-5.20.1 → workos-5.21.0}/tests/test_fga.py +97 -0
  3. {workos-5.20.1 → workos-5.21.0}/tests/test_session.py +29 -33
  4. {workos-5.20.1 → workos-5.21.0}/workos/__about__.py +1 -1
  5. {workos-5.20.1 → workos-5.21.0}/workos/fga.py +7 -3
  6. {workos-5.20.1 → workos-5.21.0}/workos/session.py +9 -22
  7. {workos-5.20.1 → workos-5.21.0}/workos/types/fga/__init__.py +1 -0
  8. {workos-5.20.1 → workos-5.21.0}/workos/types/fga/check.py +2 -0
  9. workos-5.21.0/workos/types/fga/warnings.py +33 -0
  10. {workos-5.20.1 → workos-5.21.0}/workos.egg-info/PKG-INFO +1 -1
  11. {workos-5.20.1 → workos-5.21.0}/workos.egg-info/SOURCES.txt +1 -0
  12. {workos-5.20.1 → workos-5.21.0}/LICENSE +0 -0
  13. {workos-5.20.1 → workos-5.21.0}/README.md +0 -0
  14. {workos-5.20.1 → workos-5.21.0}/setup.cfg +0 -0
  15. {workos-5.20.1 → workos-5.21.0}/setup.py +0 -0
  16. {workos-5.20.1 → workos-5.21.0}/tests/test_async_http_client.py +0 -0
  17. {workos-5.20.1 → workos-5.21.0}/tests/test_audit_logs.py +0 -0
  18. {workos-5.20.1 → workos-5.21.0}/tests/test_client.py +0 -0
  19. {workos-5.20.1 → workos-5.21.0}/tests/test_directory_sync.py +0 -0
  20. {workos-5.20.1 → workos-5.21.0}/tests/test_events.py +0 -0
  21. {workos-5.20.1 → workos-5.21.0}/tests/test_mfa.py +0 -0
  22. {workos-5.20.1 → workos-5.21.0}/tests/test_organizations.py +0 -0
  23. {workos-5.20.1 → workos-5.21.0}/tests/test_passwordless.py +0 -0
  24. {workos-5.20.1 → workos-5.21.0}/tests/test_portal.py +0 -0
  25. {workos-5.20.1 → workos-5.21.0}/tests/test_sso.py +0 -0
  26. {workos-5.20.1 → workos-5.21.0}/tests/test_sync_http_client.py +0 -0
  27. {workos-5.20.1 → workos-5.21.0}/tests/test_user_management.py +0 -0
  28. {workos-5.20.1 → workos-5.21.0}/tests/test_webhooks.py +0 -0
  29. {workos-5.20.1 → workos-5.21.0}/tests/test_widgets.py +0 -0
  30. {workos-5.20.1 → workos-5.21.0}/workos/__init__.py +0 -0
  31. {workos-5.20.1 → workos-5.21.0}/workos/_base_client.py +0 -0
  32. {workos-5.20.1 → workos-5.21.0}/workos/_client_configuration.py +0 -0
  33. {workos-5.20.1 → workos-5.21.0}/workos/async_client.py +0 -0
  34. {workos-5.20.1 → workos-5.21.0}/workos/audit_logs.py +0 -0
  35. {workos-5.20.1 → workos-5.21.0}/workos/client.py +0 -0
  36. {workos-5.20.1 → workos-5.21.0}/workos/directory_sync.py +0 -0
  37. {workos-5.20.1 → workos-5.21.0}/workos/events.py +0 -0
  38. {workos-5.20.1 → workos-5.21.0}/workos/exceptions.py +0 -0
  39. {workos-5.20.1 → workos-5.21.0}/workos/mfa.py +0 -0
  40. {workos-5.20.1 → workos-5.21.0}/workos/organizations.py +0 -0
  41. {workos-5.20.1 → workos-5.21.0}/workos/passwordless.py +0 -0
  42. {workos-5.20.1 → workos-5.21.0}/workos/portal.py +0 -0
  43. {workos-5.20.1 → workos-5.21.0}/workos/py.typed +0 -0
  44. {workos-5.20.1 → workos-5.21.0}/workos/sso.py +0 -0
  45. {workos-5.20.1 → workos-5.21.0}/workos/types/__init__.py +0 -0
  46. {workos-5.20.1 → workos-5.21.0}/workos/types/audit_logs/__init__.py +0 -0
  47. {workos-5.20.1 → workos-5.21.0}/workos/types/audit_logs/audit_log_event.py +0 -0
  48. {workos-5.20.1 → workos-5.21.0}/workos/types/audit_logs/audit_log_event_actor.py +0 -0
  49. {workos-5.20.1 → workos-5.21.0}/workos/types/audit_logs/audit_log_event_context.py +0 -0
  50. {workos-5.20.1 → workos-5.21.0}/workos/types/audit_logs/audit_log_event_target.py +0 -0
  51. {workos-5.20.1 → workos-5.21.0}/workos/types/audit_logs/audit_log_export.py +0 -0
  52. {workos-5.20.1 → workos-5.21.0}/workos/types/audit_logs/audit_log_metadata.py +0 -0
  53. {workos-5.20.1 → workos-5.21.0}/workos/types/directory_sync/__init__.py +0 -0
  54. {workos-5.20.1 → workos-5.21.0}/workos/types/directory_sync/directory.py +0 -0
  55. {workos-5.20.1 → workos-5.21.0}/workos/types/directory_sync/directory_group.py +0 -0
  56. {workos-5.20.1 → workos-5.21.0}/workos/types/directory_sync/directory_state.py +0 -0
  57. {workos-5.20.1 → workos-5.21.0}/workos/types/directory_sync/directory_type.py +0 -0
  58. {workos-5.20.1 → workos-5.21.0}/workos/types/directory_sync/directory_user.py +0 -0
  59. {workos-5.20.1 → workos-5.21.0}/workos/types/directory_sync/list_filters.py +0 -0
  60. {workos-5.20.1 → workos-5.21.0}/workos/types/events/__init__.py +0 -0
  61. {workos-5.20.1 → workos-5.21.0}/workos/types/events/authentication_payload.py +0 -0
  62. {workos-5.20.1 → workos-5.21.0}/workos/types/events/connection_payload_with_legacy_fields.py +0 -0
  63. {workos-5.20.1 → workos-5.21.0}/workos/types/events/directory_group_membership_payload.py +0 -0
  64. {workos-5.20.1 → workos-5.21.0}/workos/types/events/directory_group_with_previous_attributes.py +0 -0
  65. {workos-5.20.1 → workos-5.21.0}/workos/types/events/directory_payload.py +0 -0
  66. {workos-5.20.1 → workos-5.21.0}/workos/types/events/directory_payload_with_legacy_fields.py +0 -0
  67. {workos-5.20.1 → workos-5.21.0}/workos/types/events/directory_user_with_previous_attributes.py +0 -0
  68. {workos-5.20.1 → workos-5.21.0}/workos/types/events/event.py +0 -0
  69. {workos-5.20.1 → workos-5.21.0}/workos/types/events/event_model.py +0 -0
  70. {workos-5.20.1 → workos-5.21.0}/workos/types/events/event_type.py +0 -0
  71. {workos-5.20.1 → workos-5.21.0}/workos/types/events/list_filters.py +0 -0
  72. {workos-5.20.1 → workos-5.21.0}/workos/types/events/organization_domain_verification_failed_payload.py +0 -0
  73. {workos-5.20.1 → workos-5.21.0}/workos/types/events/previous_attributes.py +0 -0
  74. {workos-5.20.1 → workos-5.21.0}/workos/types/events/session_created_payload.py +0 -0
  75. {workos-5.20.1 → workos-5.21.0}/workos/types/fga/authorization_resource_types.py +0 -0
  76. {workos-5.20.1 → workos-5.21.0}/workos/types/fga/authorization_resources.py +0 -0
  77. {workos-5.20.1 → workos-5.21.0}/workos/types/fga/list_filters.py +0 -0
  78. {workos-5.20.1 → workos-5.21.0}/workos/types/fga/warrant.py +0 -0
  79. {workos-5.20.1 → workos-5.21.0}/workos/types/list_resource.py +0 -0
  80. {workos-5.20.1 → workos-5.21.0}/workos/types/metadata.py +0 -0
  81. {workos-5.20.1 → workos-5.21.0}/workos/types/mfa/__init__.py +0 -0
  82. {workos-5.20.1 → workos-5.21.0}/workos/types/mfa/authentication_challenge.py +0 -0
  83. {workos-5.20.1 → workos-5.21.0}/workos/types/mfa/authentication_challenge_verification_response.py +0 -0
  84. {workos-5.20.1 → workos-5.21.0}/workos/types/mfa/authentication_factor.py +0 -0
  85. {workos-5.20.1 → workos-5.21.0}/workos/types/mfa/authentication_factor_totp_and_challenge_response.py +0 -0
  86. {workos-5.20.1 → workos-5.21.0}/workos/types/mfa/enroll_authentication_factor_type.py +0 -0
  87. {workos-5.20.1 → workos-5.21.0}/workos/types/organizations/__init__.py +0 -0
  88. {workos-5.20.1 → workos-5.21.0}/workos/types/organizations/domain_data_input.py +0 -0
  89. {workos-5.20.1 → workos-5.21.0}/workos/types/organizations/list_filters.py +0 -0
  90. {workos-5.20.1 → workos-5.21.0}/workos/types/organizations/organization.py +0 -0
  91. {workos-5.20.1 → workos-5.21.0}/workos/types/organizations/organization_common.py +0 -0
  92. {workos-5.20.1 → workos-5.21.0}/workos/types/organizations/organization_domain.py +0 -0
  93. {workos-5.20.1 → workos-5.21.0}/workos/types/passwordless/__init__.py +0 -0
  94. {workos-5.20.1 → workos-5.21.0}/workos/types/passwordless/passwordless_session.py +0 -0
  95. {workos-5.20.1 → workos-5.21.0}/workos/types/passwordless/passwordless_session_type.py +0 -0
  96. {workos-5.20.1 → workos-5.21.0}/workos/types/portal/__init__.py +0 -0
  97. {workos-5.20.1 → workos-5.21.0}/workos/types/portal/portal_link.py +0 -0
  98. {workos-5.20.1 → workos-5.21.0}/workos/types/portal/portal_link_intent.py +0 -0
  99. {workos-5.20.1 → workos-5.21.0}/workos/types/portal/portal_link_intent_options.py +0 -0
  100. {workos-5.20.1 → workos-5.21.0}/workos/types/roles/__init__.py +0 -0
  101. {workos-5.20.1 → workos-5.21.0}/workos/types/roles/role.py +0 -0
  102. {workos-5.20.1 → workos-5.21.0}/workos/types/sso/__init__.py +0 -0
  103. {workos-5.20.1 → workos-5.21.0}/workos/types/sso/connection.py +0 -0
  104. {workos-5.20.1 → workos-5.21.0}/workos/types/sso/connection_domain.py +0 -0
  105. {workos-5.20.1 → workos-5.21.0}/workos/types/sso/profile.py +0 -0
  106. {workos-5.20.1 → workos-5.21.0}/workos/types/sso/sso_provider_type.py +0 -0
  107. {workos-5.20.1 → workos-5.21.0}/workos/types/user_management/__init__.py +0 -0
  108. {workos-5.20.1 → workos-5.21.0}/workos/types/user_management/authenticate_with_common.py +0 -0
  109. {workos-5.20.1 → workos-5.21.0}/workos/types/user_management/authentication_response.py +0 -0
  110. {workos-5.20.1 → workos-5.21.0}/workos/types/user_management/email_verification.py +0 -0
  111. {workos-5.20.1 → workos-5.21.0}/workos/types/user_management/impersonator.py +0 -0
  112. {workos-5.20.1 → workos-5.21.0}/workos/types/user_management/invitation.py +0 -0
  113. {workos-5.20.1 → workos-5.21.0}/workos/types/user_management/list_filters.py +0 -0
  114. {workos-5.20.1 → workos-5.21.0}/workos/types/user_management/magic_auth.py +0 -0
  115. {workos-5.20.1 → workos-5.21.0}/workos/types/user_management/oauth_tokens.py +0 -0
  116. {workos-5.20.1 → workos-5.21.0}/workos/types/user_management/organization_membership.py +0 -0
  117. {workos-5.20.1 → workos-5.21.0}/workos/types/user_management/password_hash_type.py +0 -0
  118. {workos-5.20.1 → workos-5.21.0}/workos/types/user_management/password_reset.py +0 -0
  119. {workos-5.20.1 → workos-5.21.0}/workos/types/user_management/screen_hint.py +0 -0
  120. {workos-5.20.1 → workos-5.21.0}/workos/types/user_management/session.py +0 -0
  121. {workos-5.20.1 → workos-5.21.0}/workos/types/user_management/user.py +0 -0
  122. {workos-5.20.1 → workos-5.21.0}/workos/types/user_management/user_management_provider_type.py +0 -0
  123. {workos-5.20.1 → workos-5.21.0}/workos/types/webhooks/__init__.py +0 -0
  124. {workos-5.20.1 → workos-5.21.0}/workos/types/webhooks/webhook.py +0 -0
  125. {workos-5.20.1 → workos-5.21.0}/workos/types/webhooks/webhook_model.py +0 -0
  126. {workos-5.20.1 → workos-5.21.0}/workos/types/webhooks/webhook_payload.py +0 -0
  127. {workos-5.20.1 → workos-5.21.0}/workos/types/widgets/__init__.py +0 -0
  128. {workos-5.20.1 → workos-5.21.0}/workos/types/widgets/widget_scope.py +0 -0
  129. {workos-5.20.1 → workos-5.21.0}/workos/types/widgets/widget_token_response.py +0 -0
  130. {workos-5.20.1 → workos-5.21.0}/workos/types/workos_model.py +0 -0
  131. {workos-5.20.1 → workos-5.21.0}/workos/typing/__init__.py +0 -0
  132. {workos-5.20.1 → workos-5.21.0}/workos/typing/literals.py +0 -0
  133. {workos-5.20.1 → workos-5.21.0}/workos/typing/sync_or_async.py +0 -0
  134. {workos-5.20.1 → workos-5.21.0}/workos/typing/untyped_literal.py +0 -0
  135. {workos-5.20.1 → workos-5.21.0}/workos/typing/webhooks.py +0 -0
  136. {workos-5.20.1 → workos-5.21.0}/workos/user_management.py +0 -0
  137. {workos-5.20.1 → workos-5.21.0}/workos/utils/__init__.py +0 -0
  138. {workos-5.20.1 → workos-5.21.0}/workos/utils/_base_http_client.py +0 -0
  139. {workos-5.20.1 → workos-5.21.0}/workos/utils/http_client.py +0 -0
  140. {workos-5.20.1 → workos-5.21.0}/workos/utils/pagination_order.py +0 -0
  141. {workos-5.20.1 → workos-5.21.0}/workos/utils/request_helper.py +0 -0
  142. {workos-5.20.1 → workos-5.21.0}/workos/webhooks.py +0 -0
  143. {workos-5.20.1 → workos-5.21.0}/workos/widgets.py +0 -0
  144. {workos-5.20.1 → workos-5.21.0}/workos.egg-info/dependency_links.txt +0 -0
  145. {workos-5.20.1 → workos-5.21.0}/workos.egg-info/not-zip-safe +0 -0
  146. {workos-5.20.1 → workos-5.21.0}/workos.egg-info/requires.txt +0 -0
  147. {workos-5.20.1 → workos-5.21.0}/workos.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: workos
3
- Version: 5.20.1
3
+ Version: 5.21.0
4
4
  Summary: WorkOS Python Client
5
5
  Home-page: https://github.com/workos-inc/workos-python
6
6
  Author: WorkOS
@@ -101,6 +101,103 @@ class TestErrorHandling:
101
101
  self.fga.get_resource(resource_type="test", resource_id="test")
102
102
 
103
103
 
104
+ class TestWarnings:
105
+ @pytest.fixture(autouse=True)
106
+ def setup(self, sync_http_client_for_test):
107
+ self.http_client = sync_http_client_for_test
108
+ self.fga = FGA(http_client=self.http_client)
109
+
110
+ def test_check_with_warning(self, mock_http_client_with_response):
111
+ mock_response = {
112
+ "result": "authorized",
113
+ "is_implicit": True,
114
+ "warnings": [
115
+ {
116
+ "code": "missing_context_keys",
117
+ "message": "Missing context keys",
118
+ "keys": ["key1", "key2"],
119
+ }
120
+ ],
121
+ }
122
+ mock_http_client_with_response(self.http_client, mock_response, 200)
123
+
124
+ response = self.fga.check(
125
+ op="any_of",
126
+ checks=[
127
+ WarrantCheckInput(
128
+ resource_type="schedule",
129
+ resource_id="schedule-A1",
130
+ relation="viewer",
131
+ subject=SubjectInput(resource_type="user", resource_id="user-A"),
132
+ )
133
+ ],
134
+ )
135
+ assert response.dict(exclude_none=True) == mock_response
136
+
137
+ def test_query_with_warning(self, mock_http_client_with_response):
138
+ mock_response = {
139
+ "object": "list",
140
+ "data": [
141
+ {
142
+ "resource_type": "user",
143
+ "resource_id": "richard",
144
+ "relation": "member",
145
+ "warrant": {
146
+ "resource_type": "role",
147
+ "resource_id": "developer",
148
+ "relation": "member",
149
+ "subject": {"resource_type": "user", "resource_id": "richard"},
150
+ },
151
+ "is_implicit": True,
152
+ }
153
+ ],
154
+ "list_metadata": {},
155
+ "warnings": [
156
+ {
157
+ "code": "missing_context_keys",
158
+ "message": "Missing context keys",
159
+ "keys": ["key1", "key2"],
160
+ }
161
+ ],
162
+ }
163
+
164
+ mock_http_client_with_response(self.http_client, mock_response, 200)
165
+
166
+ response = self.fga.query(
167
+ q="select member of type user for permission:view-docs",
168
+ order="asc",
169
+ warrant_token="warrant_token",
170
+ )
171
+ assert response.dict(exclude_none=True) == mock_response
172
+
173
+ def test_check_with_generic_warning(self, mock_http_client_with_response):
174
+ mock_response = {
175
+ "result": "authorized",
176
+ "is_implicit": True,
177
+ "warnings": [
178
+ {
179
+ "code": "generic",
180
+ "message": "Generic warning",
181
+ }
182
+ ],
183
+ }
184
+
185
+ mock_http_client_with_response(self.http_client, mock_response, 200)
186
+
187
+ response = self.fga.check(
188
+ op="any_of",
189
+ checks=[
190
+ WarrantCheckInput(
191
+ resource_type="schedule",
192
+ resource_id="schedule-A1",
193
+ relation="viewer",
194
+ subject=SubjectInput(resource_type="user", resource_id="user-A"),
195
+ )
196
+ ],
197
+ )
198
+ assert response.dict(exclude_none=True) == mock_response
199
+
200
+
104
201
  class TestFGA:
105
202
  @pytest.fixture(autouse=True)
106
203
  def setup(self, sync_http_client_for_test):
@@ -236,9 +236,7 @@ class TestSessionBase(SessionFixtures):
236
236
  "entitlements": ["feature_1"],
237
237
  }
238
238
 
239
- with patch.object(
240
- Session, "unseal_data", return_value=mock_session
241
- ), patch.object(session, "_is_valid_jwt", return_value=True), patch(
239
+ with patch.object(Session, "unseal_data", return_value=mock_session), patch(
242
240
  "jwt.decode", return_value=mock_jwt_payload
243
241
  ), patch.object(
244
242
  session.jwks,
@@ -324,22 +322,21 @@ class TestSession(SessionFixtures):
324
322
  cookie_password=session_constants["COOKIE_PASSWORD"],
325
323
  )
326
324
 
327
- with patch.object(session, "_is_valid_jwt", return_value=True) as _:
328
- with patch(
329
- "jwt.decode",
330
- return_value={
331
- "sid": session_constants["SESSION_ID"],
332
- "org_id": session_constants["ORGANIZATION_ID"],
333
- "role": "admin",
334
- "permissions": ["read"],
335
- "entitlements": ["feature_1"],
336
- },
337
- ):
338
- response = session.refresh()
325
+ with patch(
326
+ "jwt.decode",
327
+ return_value={
328
+ "sid": session_constants["SESSION_ID"],
329
+ "org_id": session_constants["ORGANIZATION_ID"],
330
+ "role": "admin",
331
+ "permissions": ["read"],
332
+ "entitlements": ["feature_1"],
333
+ },
334
+ ):
335
+ response = session.refresh()
339
336
 
340
- assert isinstance(response, RefreshWithSessionCookieSuccessResponse)
341
- assert response.authenticated is True
342
- assert response.user.id == session_constants["TEST_USER"]["id"]
337
+ assert isinstance(response, RefreshWithSessionCookieSuccessResponse)
338
+ assert response.authenticated is True
339
+ assert response.user.id == session_constants["TEST_USER"]["id"]
343
340
 
344
341
  # Verify the refresh token was used correctly
345
342
  mock_user_management.authenticate_with_refresh_token.assert_called_once_with(
@@ -425,22 +422,21 @@ class TestAsyncSession(SessionFixtures):
425
422
  cookie_password=session_constants["COOKIE_PASSWORD"],
426
423
  )
427
424
 
428
- with patch.object(session, "_is_valid_jwt", return_value=True) as _:
429
- with patch(
430
- "jwt.decode",
431
- return_value={
432
- "sid": session_constants["SESSION_ID"],
433
- "org_id": session_constants["ORGANIZATION_ID"],
434
- "role": "admin",
435
- "permissions": ["read"],
436
- "entitlements": ["feature_1"],
437
- },
438
- ):
439
- response = await session.refresh()
425
+ with patch(
426
+ "jwt.decode",
427
+ return_value={
428
+ "sid": session_constants["SESSION_ID"],
429
+ "org_id": session_constants["ORGANIZATION_ID"],
430
+ "role": "admin",
431
+ "permissions": ["read"],
432
+ "entitlements": ["feature_1"],
433
+ },
434
+ ):
435
+ response = await session.refresh()
440
436
 
441
- assert isinstance(response, RefreshWithSessionCookieSuccessResponse)
442
- assert response.authenticated is True
443
- assert response.user.id == session_constants["TEST_USER"]["id"]
437
+ assert isinstance(response, RefreshWithSessionCookieSuccessResponse)
438
+ assert response.authenticated is True
439
+ assert response.user.id == session_constants["TEST_USER"]["id"]
444
440
 
445
441
  # Verify the refresh token was used correctly
446
442
  mock_user_management.authenticate_with_refresh_token.assert_called_once_with(
@@ -12,7 +12,7 @@ __package_name__ = "workos"
12
12
 
13
13
  __package_url__ = "https://github.com/workos-inc/workos-python"
14
14
 
15
- __version__ = "5.20.1"
15
+ __version__ = "5.21.0"
16
16
 
17
17
  __author__ = "WorkOS"
18
18
 
@@ -11,6 +11,7 @@ from workos.types.fga import (
11
11
  WarrantWriteOperation,
12
12
  WriteWarrantResponse,
13
13
  WarrantQueryResult,
14
+ FGAWarning,
14
15
  )
15
16
  from workos.types.fga.list_filters import (
16
17
  AuthorizationResourceListFilters,
@@ -45,9 +46,11 @@ AuthorizationResourceTypeListResource = WorkOSListResource[
45
46
 
46
47
  WarrantListResource = WorkOSListResource[Warrant, WarrantListFilters, ListMetadata]
47
48
 
48
- WarrantQueryListResource = WorkOSListResource[
49
- WarrantQueryResult, WarrantQueryListFilters, ListMetadata
50
- ]
49
+
50
+ class WarrantQueryListResource(
51
+ WorkOSListResource[WarrantQueryResult, WarrantQueryListFilters, ListMetadata]
52
+ ):
53
+ warnings: Optional[Sequence[FGAWarning]] = None
51
54
 
52
55
 
53
56
  class FGAModule(Protocol):
@@ -641,5 +644,6 @@ class FGA(FGAModule):
641
644
  return WarrantQueryListResource(
642
645
  list_method=self.query,
643
646
  list_args=list_params,
647
+ warnings=response.get("warnings"),
644
648
  **ListPage[WarrantQueryResult](**response).model_dump(),
645
649
  )
@@ -77,20 +77,20 @@ class SessionModule(Protocol):
77
77
  reason=AuthenticateWithSessionCookieFailureReason.INVALID_SESSION_COOKIE,
78
78
  )
79
79
 
80
- if not self._is_valid_jwt(session["access_token"]):
80
+ try:
81
+ signing_key = self.jwks.get_signing_key_from_jwt(session["access_token"])
82
+ decoded = jwt.decode(
83
+ session["access_token"],
84
+ signing_key.key,
85
+ algorithms=self.jwk_algorithms,
86
+ options={"verify_aud": False},
87
+ )
88
+ except jwt.exceptions.InvalidTokenError:
81
89
  return AuthenticateWithSessionCookieErrorResponse(
82
90
  authenticated=False,
83
91
  reason=AuthenticateWithSessionCookieFailureReason.INVALID_JWT,
84
92
  )
85
93
 
86
- signing_key = self.jwks.get_signing_key_from_jwt(session["access_token"])
87
- decoded = jwt.decode(
88
- session["access_token"],
89
- signing_key.key,
90
- algorithms=self.jwk_algorithms,
91
- options={"verify_aud": False},
92
- )
93
-
94
94
  return AuthenticateWithSessionCookieSuccessResponse(
95
95
  authenticated=True,
96
96
  session_id=decoded["sid"],
@@ -128,19 +128,6 @@ class SessionModule(Protocol):
128
128
  )
129
129
  return str(result)
130
130
 
131
- def _is_valid_jwt(self, token: str) -> bool:
132
- try:
133
- signing_key = self.jwks.get_signing_key_from_jwt(token)
134
- jwt.decode(
135
- token,
136
- signing_key.key,
137
- algorithms=self.jwk_algorithms,
138
- options={"verify_aud": False},
139
- )
140
- return True
141
- except jwt.exceptions.InvalidTokenError:
142
- return False
143
-
144
131
  @staticmethod
145
132
  def seal_data(data: Dict[str, Any], key: str) -> str:
146
133
  fernet = Fernet(key)
@@ -2,3 +2,4 @@ from .check import *
2
2
  from .authorization_resource_types import *
3
3
  from .authorization_resources import *
4
4
  from .warrant import *
5
+ from .warnings import *
@@ -3,6 +3,7 @@ from typing import Any, Literal, Mapping, Optional, Sequence, TypedDict
3
3
  from workos.types.workos_model import WorkOSModel
4
4
  from workos.typing.literals import LiteralOrUntyped
5
5
 
6
+ from .warnings import FGAWarning
6
7
  from .warrant import Subject, SubjectInput
7
8
 
8
9
  CheckOperation = Literal["any_of", "all_of", "batch"]
@@ -44,6 +45,7 @@ class CheckResponse(WorkOSModel):
44
45
  result: LiteralOrUntyped[CheckResult]
45
46
  is_implicit: bool
46
47
  debug_info: Optional[DebugInfo] = None
48
+ warnings: Optional[Sequence[FGAWarning]] = None
47
49
 
48
50
  def authorized(self) -> bool:
49
51
  return self.result == "authorized"
@@ -0,0 +1,33 @@
1
+ from typing import Sequence, Union, Any, Dict, Literal
2
+ from typing_extensions import Annotated
3
+
4
+ from pydantic import BeforeValidator
5
+ from pydantic_core.core_schema import ValidationInfo
6
+
7
+ from workos.types.workos_model import WorkOSModel
8
+
9
+
10
+ class FGABaseWarning(WorkOSModel):
11
+ code: str
12
+ message: str
13
+
14
+
15
+ class MissingContextKeysWarning(FGABaseWarning):
16
+ code: Literal["missing_context_keys"]
17
+ keys: Sequence[str]
18
+
19
+
20
+ def fga_warning_dispatch_validator(
21
+ value: Dict[str, Any], info: ValidationInfo
22
+ ) -> FGABaseWarning:
23
+ if value.get("code") == "missing_context_keys":
24
+ return MissingContextKeysWarning.model_validate(value)
25
+
26
+ # Fallback to the base warning model
27
+ return FGABaseWarning.model_validate(value)
28
+
29
+
30
+ FGAWarning = Annotated[
31
+ Union[MissingContextKeysWarning, FGABaseWarning],
32
+ BeforeValidator(fga_warning_dispatch_validator),
33
+ ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: workos
3
- Version: 5.20.1
3
+ Version: 5.21.0
4
4
  Summary: WorkOS Python Client
5
5
  Home-page: https://github.com/workos-inc/workos-python
6
6
  Author: WorkOS
@@ -82,6 +82,7 @@ workos/types/fga/authorization_resource_types.py
82
82
  workos/types/fga/authorization_resources.py
83
83
  workos/types/fga/check.py
84
84
  workos/types/fga/list_filters.py
85
+ workos/types/fga/warnings.py
85
86
  workos/types/fga/warrant.py
86
87
  workos/types/mfa/__init__.py
87
88
  workos/types/mfa/authentication_challenge.py
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes