workos 7.1.2 → 8.0.1
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.
- checksums.yaml +4 -4
- data/.github/workflows/docs.yml +49 -0
- data/.github/workflows/release-please.yml +2 -2
- data/.gitignore +2 -0
- data/.last-synced-sha +1 -1
- data/.oagen-manifest.json +61 -40
- data/.release-please-manifest.json +1 -1
- data/.yardopts +6 -0
- data/CHANGELOG.md +36 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +33 -2
- data/README.md +19 -0
- data/docs/V7_MIGRATION_GUIDE.md +21 -0
- data/lib/workos/actions.rb +1 -1
- data/lib/workos/api_keys/api_key.rb +1 -1
- data/lib/workos/api_keys/api_key_created_data.rb +1 -1
- data/lib/workos/api_keys/organization_api_key.rb +43 -0
- data/lib/workos/{types/events_order.rb → api_keys/organization_api_key_owner.rb} +1 -3
- data/lib/workos/api_keys/organization_api_key_with_value.rb +46 -0
- data/lib/workos/{types/audit_logs_order.rb → api_keys/organization_api_key_with_value_owner.rb} +1 -3
- data/lib/workos/api_keys.rb +46 -46
- data/lib/workos/audit_logs.rb +4 -4
- data/lib/workos/authorization/user_organization_membership_base_list_data.rb +5 -2
- data/lib/workos/authorization/{role_assignment.rb → user_role_assignment.rb} +5 -2
- data/lib/workos/authorization/{role_assignment_resource.rb → user_role_assignment_resource.rb} +1 -1
- data/lib/workos/authorization.rb +122 -22
- data/lib/workos/base_client.rb +71 -5
- data/lib/workos/client.rb +4 -4
- data/lib/workos/connect.rb +2 -2
- data/lib/workos/directory_sync/directory_user.rb +3 -0
- data/lib/workos/directory_sync/directory_user_with_groups.rb +4 -1
- data/lib/workos/directory_sync/dsync_user_updated_data.rb +3 -0
- data/lib/workos/directory_sync.rb +6 -6
- data/lib/workos/encryptors/aes_gcm.rb +19 -5
- data/lib/workos/events.rb +2 -2
- data/lib/workos/feature_flags.rb +6 -6
- data/lib/workos/groups.rb +4 -4
- data/lib/workos/multi_factor_auth.rb +2 -2
- data/lib/workos/organizations.rb +2 -2
- data/lib/workos/session.rb +28 -7
- data/lib/workos/session_manager.rb +24 -1
- data/lib/workos/sso/profile.rb +3 -0
- data/lib/workos/sso.rb +2 -2
- data/lib/workos/types/event_context_actor_source.rb +2 -1
- data/lib/workos/types/{applications_order.rb → pagination_order.rb} +1 -1
- data/lib/workos/types/{vault_byok_key_verification_completed_data_key_provider.rb → vault_byok_key_provider.rb} +1 -1
- data/lib/workos/user_management/create_user_api_key.rb +25 -0
- data/lib/workos/user_management/organization_membership.rb +5 -2
- data/lib/workos/user_management/user_api_key.rb +43 -0
- data/lib/workos/user_management/user_api_key_created_data_owner.rb +25 -0
- data/lib/workos/{api_keys/api_key_with_value_owner.rb → user_management/user_api_key_owner.rb} +1 -1
- data/lib/workos/{types/webhooks_order.rb → user_management/user_api_key_revoked_data_owner.rb} +1 -3
- data/lib/workos/{api_keys/api_key_with_value.rb → user_management/user_api_key_with_value.rb} +2 -2
- data/lib/workos/{types/groups_order.rb → user_management/user_api_key_with_value_owner.rb} +1 -3
- data/lib/workos/user_management/user_organization_membership.rb +5 -2
- data/lib/workos/user_management.rb +114 -10
- data/lib/workos/user_management_organization_membership_groups.rb +2 -2
- data/lib/workos/vault/vault_byok_key_deleted.rb +34 -0
- data/lib/workos/vault/vault_byok_key_deleted_data.rb +22 -0
- data/lib/workos/version.rb +1 -1
- data/lib/workos/webhooks.rb +3 -3
- data/rbi/workos/api_key.rbi +2 -2
- data/rbi/workos/api_key_created_data.rbi +2 -2
- data/rbi/workos/api_key_revoked_data.rbi +2 -2
- data/rbi/workos/api_keys.rbi +17 -17
- data/rbi/workos/authorization.rbi +27 -1
- data/rbi/workos/client.rbi +3 -3
- data/rbi/workos/create_user_api_key.rbi +36 -0
- data/rbi/workos/directory_user.rbi +6 -0
- data/rbi/workos/directory_user_with_groups.rbi +6 -0
- data/rbi/workos/dsync_user_updated_data.rbi +6 -0
- data/rbi/workos/organization_api_key.rbi +72 -0
- data/rbi/workos/{api_key_with_value_owner.rbi → organization_api_key_owner.rbi} +1 -1
- data/rbi/workos/organization_api_key_with_value.rbi +78 -0
- data/rbi/workos/organization_api_key_with_value_owner.rbi +30 -0
- data/rbi/workos/organization_membership.rbi +6 -0
- data/rbi/workos/profile.rbi +6 -0
- data/rbi/workos/user_api_key.rbi +72 -0
- data/rbi/workos/user_api_key_created_data_owner.rbi +36 -0
- data/rbi/workos/user_api_key_owner.rbi +36 -0
- data/rbi/workos/user_api_key_revoked_data_owner.rbi +36 -0
- data/rbi/workos/{api_key_with_value.rbi → user_api_key_with_value.rbi} +3 -3
- data/rbi/workos/user_api_key_with_value_owner.rbi +36 -0
- data/rbi/workos/user_management.rbi +31 -0
- data/rbi/workos/user_organization_membership.rbi +6 -0
- data/rbi/workos/user_organization_membership_base_list_data.rbi +6 -0
- data/rbi/workos/{role_assignment.rbi → user_role_assignment.rbi} +9 -3
- data/rbi/workos/{role_assignment_resource.rbi → user_role_assignment_resource.rbi} +1 -1
- data/rbi/workos/vault_byok_key_deleted.rbi +54 -0
- data/rbi/workos/vault_byok_key_deleted_data.rbi +30 -0
- data/script/docs +16 -0
- data/script/docs-serve +12 -0
- data/script/llms-txt +37 -0
- data/test/workos/test_actions.rb +9 -0
- data/test/workos/test_api_keys.rb +17 -17
- data/test/workos/test_authorization.rb +16 -0
- data/test/workos/test_base_client.rb +44 -0
- data/test/workos/test_encryptors_aes_gcm.rb +16 -1
- data/test/workos/test_model_round_trip.rb +278 -83
- data/test/workos/test_session.rb +43 -4
- data/test/workos/test_user_management.rb +25 -1
- data/test/workos/test_webhook_verify.rb +11 -0
- metadata +39 -33
- data/lib/workos/types/authorization_order.rb +0 -9
- data/lib/workos/types/connections_order.rb +0 -9
- data/lib/workos/types/directories_order.rb +0 -9
- data/lib/workos/types/directory_groups_order.rb +0 -9
- data/lib/workos/types/directory_users_order.rb +0 -9
- data/lib/workos/types/feature_flags_order.rb +0 -9
- data/lib/workos/types/organizations_api_keys_order.rb +0 -9
- data/lib/workos/types/organizations_feature_flags_order.rb +0 -9
- data/lib/workos/types/organizations_order.rb +0 -9
- data/lib/workos/types/permissions_order.rb +0 -9
- data/lib/workos/types/user_management_invitations_order.rb +0 -9
- data/lib/workos/types/user_management_multi_factor_authentication_order.rb +0 -9
- data/lib/workos/types/user_management_organization_membership_groups_order.rb +0 -9
- data/lib/workos/types/user_management_organization_membership_order.rb +0 -9
- data/lib/workos/types/user_management_users_authorized_applications_order.rb +0 -9
- data/lib/workos/types/user_management_users_feature_flags_order.rb +0 -9
- data/lib/workos/types/user_management_users_order.rb +0 -9
data/lib/workos/base_client.rb
CHANGED
|
@@ -134,7 +134,8 @@ module WorkOS
|
|
|
134
134
|
attempt = 0
|
|
135
135
|
|
|
136
136
|
loop do
|
|
137
|
-
|
|
137
|
+
loggable_path = redact_path(request.path)
|
|
138
|
+
log(:debug, "request start", method: request.method, path: loggable_path, attempt: attempt + 1)
|
|
138
139
|
http = connection_for(base, timeout)
|
|
139
140
|
response = http.request(request)
|
|
140
141
|
return response if response.is_a?(Net::HTTPSuccess)
|
|
@@ -142,11 +143,11 @@ module WorkOS
|
|
|
142
143
|
if attempt < retries && retryable?(response)
|
|
143
144
|
attempt += 1
|
|
144
145
|
inject_retry_idempotency_key(request)
|
|
145
|
-
log(:info, "request retry", method: request.method, path:
|
|
146
|
+
log(:info, "request retry", method: request.method, path: loggable_path, attempt: attempt + 1, status: response.code.to_i)
|
|
146
147
|
sleep(retry_delay(response, attempt))
|
|
147
148
|
next
|
|
148
149
|
end
|
|
149
|
-
log(:warn, "request error", method: request.method, path:
|
|
150
|
+
log(:warn, "request error", method: request.method, path: loggable_path, status: response.code.to_i, request_id: response["x-request-id"] || response["X-Request-Id"])
|
|
150
151
|
handle_error_response(response)
|
|
151
152
|
rescue Net::OpenTimeout, Net::ReadTimeout,
|
|
152
153
|
Errno::ECONNRESET, Errno::ECONNREFUSED,
|
|
@@ -155,11 +156,11 @@ module WorkOS
|
|
|
155
156
|
if attempt < retries
|
|
156
157
|
attempt += 1
|
|
157
158
|
inject_retry_idempotency_key(request)
|
|
158
|
-
log(:info, "request retry", method: request.method, path:
|
|
159
|
+
log(:info, "request retry", method: request.method, path: loggable_path, attempt: attempt + 1, error: e.class.name)
|
|
159
160
|
sleep(retry_delay(nil, attempt))
|
|
160
161
|
next
|
|
161
162
|
end
|
|
162
|
-
log(:warn, "connection error", method: request.method, path:
|
|
163
|
+
log(:warn, "connection error", method: request.method, path: loggable_path, error: e.class.name, message: e.message)
|
|
163
164
|
raise WorkOS::APIConnectionError.new(message: e.message)
|
|
164
165
|
end
|
|
165
166
|
end
|
|
@@ -179,6 +180,71 @@ module WorkOS
|
|
|
179
180
|
|
|
180
181
|
private
|
|
181
182
|
|
|
183
|
+
# Redact path segments that carry bearer-equivalent tokens (e.g.
|
|
184
|
+
# `/user_management/invitations/by_token/<token>`,
|
|
185
|
+
# `/user_management/magic_auth/<token>`, password-reset / email-
|
|
186
|
+
# verification token paths) before the path is written to a logger.
|
|
187
|
+
# The WorkOS API exposes a small number of "by_token" endpoints whose
|
|
188
|
+
# path segments are themselves authentication material; redacting them
|
|
189
|
+
# here means the SDK never emits the token in its own log/retry/error
|
|
190
|
+
# messages even when the host application configures verbose logging.
|
|
191
|
+
REDACTED_TOKEN_PREFIXES = %w[
|
|
192
|
+
/user_management/invitations/by_token
|
|
193
|
+
/user_management/magic_auth
|
|
194
|
+
/user_management/password_reset
|
|
195
|
+
/user_management/email_verification
|
|
196
|
+
].freeze
|
|
197
|
+
private_constant :REDACTED_TOKEN_PREFIXES
|
|
198
|
+
|
|
199
|
+
# Query-string keys whose values are bearer-equivalent or otherwise
|
|
200
|
+
# sensitive and should never appear in SDK log lines. Defense-in-depth
|
|
201
|
+
# for any path that flows through execute_request with a sensitive
|
|
202
|
+
# query parameter (most WorkOS-issued tokens are path segments or POST
|
|
203
|
+
# bodies, but a few flows — e.g. authorize/logout redirects — surface
|
|
204
|
+
# them in the query string).
|
|
205
|
+
REDACTED_QUERY_KEYS = %w[
|
|
206
|
+
token
|
|
207
|
+
code
|
|
208
|
+
code_challenge
|
|
209
|
+
code_verifier
|
|
210
|
+
session_id
|
|
211
|
+
refresh_token
|
|
212
|
+
access_token
|
|
213
|
+
].freeze
|
|
214
|
+
private_constant :REDACTED_QUERY_KEYS
|
|
215
|
+
|
|
216
|
+
def redact_path(path)
|
|
217
|
+
return path if path.nil? || path.empty?
|
|
218
|
+
|
|
219
|
+
# Strip query string for the prefix match; reattach (scrubbed) after.
|
|
220
|
+
path_only, query = path.split("?", 2)
|
|
221
|
+
REDACTED_TOKEN_PREFIXES.each do |prefix|
|
|
222
|
+
next unless path_only.start_with?("#{prefix}/")
|
|
223
|
+
|
|
224
|
+
# Replace every segment after the matched prefix with "[REDACTED]".
|
|
225
|
+
remainder = path_only[(prefix.length + 1)..]
|
|
226
|
+
next if remainder.nil? || remainder.empty?
|
|
227
|
+
|
|
228
|
+
redacted = remainder.split("/").map { "[REDACTED]" }.join("/")
|
|
229
|
+
path_only = "#{prefix}/#{redacted}"
|
|
230
|
+
break
|
|
231
|
+
end
|
|
232
|
+
query ? "#{path_only}?#{redact_query(query)}" : path_only
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def redact_query(query)
|
|
236
|
+
return query if query.nil? || query.empty?
|
|
237
|
+
|
|
238
|
+
query.split("&").map { |pair|
|
|
239
|
+
key, value = pair.split("=", 2)
|
|
240
|
+
if value && !value.empty? && REDACTED_QUERY_KEYS.include?(key)
|
|
241
|
+
"#{key}=[REDACTED]"
|
|
242
|
+
else
|
|
243
|
+
pair
|
|
244
|
+
end
|
|
245
|
+
}.join("&")
|
|
246
|
+
end
|
|
247
|
+
|
|
182
248
|
def append_query(path, params)
|
|
183
249
|
return path unless params.is_a?(Hash) && !params.empty?
|
|
184
250
|
|
data/lib/workos/client.rb
CHANGED
|
@@ -4,10 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
module WorkOS
|
|
6
6
|
class Client < BaseClient
|
|
7
|
-
def api_keys
|
|
8
|
-
@api_keys ||= WorkOS::ApiKeys.new(self)
|
|
9
|
-
end
|
|
10
|
-
|
|
11
7
|
def multi_factor_auth
|
|
12
8
|
@multi_factor_auth ||= WorkOS::MultiFactorAuth.new(self)
|
|
13
9
|
end
|
|
@@ -48,6 +44,10 @@ module WorkOS
|
|
|
48
44
|
@organizations ||= WorkOS::Organizations.new(self)
|
|
49
45
|
end
|
|
50
46
|
|
|
47
|
+
def api_keys
|
|
48
|
+
@api_keys ||= WorkOS::ApiKeys.new(self)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
51
|
def groups
|
|
52
52
|
@groups ||= WorkOS::Groups.new(self)
|
|
53
53
|
end
|
data/lib/workos/connect.rb
CHANGED
|
@@ -43,14 +43,14 @@ module WorkOS
|
|
|
43
43
|
# @param before [String, nil] An object ID that defines your place in the list. When the ID is not present, you are at the end of the list. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`.
|
|
44
44
|
# @param after [String, nil] An object ID that defines your place in the list. When the ID is not present, you are at the end of the list. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`.
|
|
45
45
|
# @param limit [Integer, nil] Upper limit on the number of objects to return, between `1` and `100`.
|
|
46
|
-
# @param order [WorkOS::Types::
|
|
46
|
+
# @param order [WorkOS::Types::PaginationOrder, nil] Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending.
|
|
47
47
|
# @param organization_id [String, nil] Filter Connect Applications by organization ID.
|
|
48
48
|
# @param request_options [Hash] (see WorkOS::Types::RequestOptions)
|
|
49
49
|
# @return [WorkOS::Types::ListStruct<WorkOS::ConnectApplication>]
|
|
50
50
|
def list_applications(
|
|
51
51
|
before: nil,
|
|
52
52
|
after: nil,
|
|
53
|
-
limit:
|
|
53
|
+
limit: 10,
|
|
54
54
|
order: "desc",
|
|
55
55
|
organization_id: nil,
|
|
56
56
|
request_options: {}
|
|
@@ -13,6 +13,7 @@ module WorkOS
|
|
|
13
13
|
email: :email,
|
|
14
14
|
first_name: :first_name,
|
|
15
15
|
last_name: :last_name,
|
|
16
|
+
name: :name,
|
|
16
17
|
emails: :emails,
|
|
17
18
|
job_title: :job_title,
|
|
18
19
|
username: :username,
|
|
@@ -43,6 +44,7 @@ module WorkOS
|
|
|
43
44
|
:email,
|
|
44
45
|
:first_name,
|
|
45
46
|
:last_name,
|
|
47
|
+
:name,
|
|
46
48
|
:state,
|
|
47
49
|
:custom_attributes,
|
|
48
50
|
:role,
|
|
@@ -88,6 +90,7 @@ module WorkOS
|
|
|
88
90
|
@email = hash[:email]
|
|
89
91
|
@first_name = hash[:first_name]
|
|
90
92
|
@last_name = hash[:last_name]
|
|
93
|
+
@name = hash[:name]
|
|
91
94
|
@emails = (hash[:emails] || []).map { |item| item ? WorkOS::DirectoryUserEmail.new(item) : nil }
|
|
92
95
|
@job_title = hash[:job_title]
|
|
93
96
|
@username = hash[:username]
|
|
@@ -13,6 +13,7 @@ module WorkOS
|
|
|
13
13
|
email: :email,
|
|
14
14
|
first_name: :first_name,
|
|
15
15
|
last_name: :last_name,
|
|
16
|
+
name: :name,
|
|
16
17
|
emails: :emails,
|
|
17
18
|
job_title: :job_title,
|
|
18
19
|
username: :username,
|
|
@@ -35,7 +36,7 @@ module WorkOS
|
|
|
35
36
|
# @!attribute raw_attributes
|
|
36
37
|
# @deprecated The raw attributes received from the directory provider.
|
|
37
38
|
# @!attribute groups
|
|
38
|
-
# @deprecated The directory groups the user belongs to. Use the List Directory Groups endpoint with a user filter
|
|
39
|
+
# @deprecated The directory groups the user belongs to. Deprecated: starting May 1, 2026, this field returns an empty array by default for newly created teams. Existing teams currently depending on this field should migrate to the new access pattern for better throughput performance — the field is unbounded by user, so users with many group memberships produce large, slow response payloads. Use the List Directory Groups endpoint with a `user` filter to fetch a user's group memberships.
|
|
39
40
|
|
|
40
41
|
attr_accessor \
|
|
41
42
|
:object,
|
|
@@ -46,6 +47,7 @@ module WorkOS
|
|
|
46
47
|
:email,
|
|
47
48
|
:first_name,
|
|
48
49
|
:last_name,
|
|
50
|
+
:name,
|
|
49
51
|
:state,
|
|
50
52
|
:custom_attributes,
|
|
51
53
|
:role,
|
|
@@ -98,6 +100,7 @@ module WorkOS
|
|
|
98
100
|
@email = hash[:email]
|
|
99
101
|
@first_name = hash[:first_name]
|
|
100
102
|
@last_name = hash[:last_name]
|
|
103
|
+
@name = hash[:name]
|
|
101
104
|
@emails = (hash[:emails] || []).map { |item| item ? WorkOS::DirectoryUserWithGroupsEmail.new(item) : nil }
|
|
102
105
|
@job_title = hash[:job_title]
|
|
103
106
|
@username = hash[:username]
|
|
@@ -13,6 +13,7 @@ module WorkOS
|
|
|
13
13
|
email: :email,
|
|
14
14
|
first_name: :first_name,
|
|
15
15
|
last_name: :last_name,
|
|
16
|
+
name: :name,
|
|
16
17
|
emails: :emails,
|
|
17
18
|
job_title: :job_title,
|
|
18
19
|
username: :username,
|
|
@@ -44,6 +45,7 @@ module WorkOS
|
|
|
44
45
|
:email,
|
|
45
46
|
:first_name,
|
|
46
47
|
:last_name,
|
|
48
|
+
:name,
|
|
47
49
|
:state,
|
|
48
50
|
:custom_attributes,
|
|
49
51
|
:role,
|
|
@@ -90,6 +92,7 @@ module WorkOS
|
|
|
90
92
|
@email = hash[:email]
|
|
91
93
|
@first_name = hash[:first_name]
|
|
92
94
|
@last_name = hash[:last_name]
|
|
95
|
+
@name = hash[:name]
|
|
93
96
|
@emails = (hash[:emails] || []).map { |item| item ? WorkOS::DsyncUserUpdatedDataEmail.new(item) : nil }
|
|
94
97
|
@job_title = hash[:job_title]
|
|
95
98
|
@username = hash[:username]
|
|
@@ -14,7 +14,7 @@ module WorkOS
|
|
|
14
14
|
# @param before [String, nil] An object ID that defines your place in the list. When the ID is not present, you are at the end of the list.
|
|
15
15
|
# @param after [String, nil] An object ID that defines your place in the list. When the ID is not present, you are at the end of the list.
|
|
16
16
|
# @param limit [Integer, nil] Upper limit on the number of objects to return, between `1` and `100`.
|
|
17
|
-
# @param order [WorkOS::Types::
|
|
17
|
+
# @param order [WorkOS::Types::PaginationOrder, nil] Order the results by the creation time.
|
|
18
18
|
# @param organization_id [String, nil] Filter Directories by their associated organization.
|
|
19
19
|
# @param search [String, nil] Searchable text to match against Directory names.
|
|
20
20
|
# @param domain [String, nil] (deprecated) Filter Directories by their associated domain.
|
|
@@ -23,7 +23,7 @@ module WorkOS
|
|
|
23
23
|
def list_directories(
|
|
24
24
|
before: nil,
|
|
25
25
|
after: nil,
|
|
26
|
-
limit:
|
|
26
|
+
limit: 10,
|
|
27
27
|
order: "desc",
|
|
28
28
|
organization_id: nil,
|
|
29
29
|
search: nil,
|
|
@@ -106,7 +106,7 @@ module WorkOS
|
|
|
106
106
|
# @param before [String, nil] An object ID that defines your place in the list. When the ID is not present, you are at the end of the list. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`.
|
|
107
107
|
# @param after [String, nil] An object ID that defines your place in the list. When the ID is not present, you are at the end of the list. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`.
|
|
108
108
|
# @param limit [Integer, nil] Upper limit on the number of objects to return, between `1` and `100`.
|
|
109
|
-
# @param order [WorkOS::Types::
|
|
109
|
+
# @param order [WorkOS::Types::PaginationOrder, nil] Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending.
|
|
110
110
|
# @param directory [String, nil] Unique identifier of the WorkOS Directory. This value can be obtained from the WorkOS dashboard or from the WorkOS API.
|
|
111
111
|
# @param user [String, nil] Unique identifier of the WorkOS Directory User. This value can be obtained from the WorkOS API.
|
|
112
112
|
# @param request_options [Hash] (see WorkOS::Types::RequestOptions)
|
|
@@ -114,7 +114,7 @@ module WorkOS
|
|
|
114
114
|
def list_groups(
|
|
115
115
|
before: nil,
|
|
116
116
|
after: nil,
|
|
117
|
-
limit:
|
|
117
|
+
limit: 10,
|
|
118
118
|
order: "desc",
|
|
119
119
|
directory: nil,
|
|
120
120
|
user: nil,
|
|
@@ -177,7 +177,7 @@ module WorkOS
|
|
|
177
177
|
# @param before [String, nil] An object ID that defines your place in the list. When the ID is not present, you are at the end of the list. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`.
|
|
178
178
|
# @param after [String, nil] An object ID that defines your place in the list. When the ID is not present, you are at the end of the list. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`.
|
|
179
179
|
# @param limit [Integer, nil] Upper limit on the number of objects to return, between `1` and `100`.
|
|
180
|
-
# @param order [WorkOS::Types::
|
|
180
|
+
# @param order [WorkOS::Types::PaginationOrder, nil] Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending.
|
|
181
181
|
# @param directory [String, nil] Unique identifier of the WorkOS Directory. This value can be obtained from the WorkOS dashboard or from the WorkOS API.
|
|
182
182
|
# @param group [String, nil] Unique identifier of the WorkOS Directory Group. This value can be obtained from the WorkOS API.
|
|
183
183
|
# @param request_options [Hash] (see WorkOS::Types::RequestOptions)
|
|
@@ -185,7 +185,7 @@ module WorkOS
|
|
|
185
185
|
def list_users(
|
|
186
186
|
before: nil,
|
|
187
187
|
after: nil,
|
|
188
|
-
limit:
|
|
188
|
+
limit: 10,
|
|
189
189
|
order: "desc",
|
|
190
190
|
directory: nil,
|
|
191
191
|
group: nil,
|
|
@@ -14,8 +14,14 @@ module WorkOS
|
|
|
14
14
|
module Encryptors
|
|
15
15
|
class AesGcm
|
|
16
16
|
SEAL_VERSION = 0x01
|
|
17
|
+
# Minimum cookie_password byte length. AES-256-GCM derives a 32-byte
|
|
18
|
+
# key from the password via SHA-256; a passphrase shorter than the
|
|
19
|
+
# output it derives to provides less than the full keyspace and makes
|
|
20
|
+
# offline brute-force feasible. See README + V7_MIGRATION_GUIDE.md.
|
|
21
|
+
MIN_KEY_BYTES = 32
|
|
17
22
|
|
|
18
23
|
def seal(data, key)
|
|
24
|
+
validate_key!(key)
|
|
19
25
|
json = data.is_a?(String) ? data : JSON.generate(data)
|
|
20
26
|
cipher = OpenSSL::Cipher.new("aes-256-gcm").encrypt
|
|
21
27
|
cipher.key = derive_key(key)
|
|
@@ -26,13 +32,16 @@ module WorkOS
|
|
|
26
32
|
end
|
|
27
33
|
|
|
28
34
|
def unseal(sealed, key)
|
|
35
|
+
validate_key!(key)
|
|
29
36
|
raw = Base64.decode64(sealed.to_s)
|
|
30
|
-
decode_v7(raw, key)
|
|
31
|
-
rescue ArgumentError, OpenSSL::Cipher::CipherError => original_error
|
|
32
37
|
begin
|
|
33
|
-
|
|
34
|
-
rescue ArgumentError, OpenSSL::Cipher::CipherError
|
|
35
|
-
|
|
38
|
+
decode_v7(raw, key)
|
|
39
|
+
rescue ArgumentError, OpenSSL::Cipher::CipherError => original_error
|
|
40
|
+
begin
|
|
41
|
+
decode_old(raw, key)
|
|
42
|
+
rescue ArgumentError, OpenSSL::Cipher::CipherError
|
|
43
|
+
raise original_error
|
|
44
|
+
end
|
|
36
45
|
end
|
|
37
46
|
end
|
|
38
47
|
|
|
@@ -83,6 +92,11 @@ module WorkOS
|
|
|
83
92
|
def derive_key(passphrase)
|
|
84
93
|
Digest::SHA256.digest(passphrase.to_s)
|
|
85
94
|
end
|
|
95
|
+
|
|
96
|
+
def validate_key!(key)
|
|
97
|
+
raise ArgumentError, "cookie_password is required" if key.nil? || key.to_s.empty?
|
|
98
|
+
raise ArgumentError, "cookie_password must be at least #{MIN_KEY_BYTES} bytes" if key.to_s.bytesize < MIN_KEY_BYTES
|
|
99
|
+
end
|
|
86
100
|
end
|
|
87
101
|
end
|
|
88
102
|
end
|
data/lib/workos/events.rb
CHANGED
|
@@ -14,7 +14,7 @@ module WorkOS
|
|
|
14
14
|
# @param before [String, nil] An object ID that defines your place in the list. When the ID is not present, you are at the end of the list. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`.
|
|
15
15
|
# @param after [String, nil] An object ID that defines your place in the list. When the ID is not present, you are at the end of the list. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`.
|
|
16
16
|
# @param limit [Integer, nil] Upper limit on the number of objects to return, between `1` and `100`.
|
|
17
|
-
# @param order [WorkOS::Types::
|
|
17
|
+
# @param order [WorkOS::Types::PaginationOrder, nil] Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending.
|
|
18
18
|
# @param events [Array<String>, nil] Filter events by one or more event types (e.g. `dsync.user.created`).
|
|
19
19
|
# @param range_start [String, nil] ISO-8601 date string to filter events created after this date.
|
|
20
20
|
# @param range_end [String, nil] ISO-8601 date string to filter events created before this date.
|
|
@@ -24,7 +24,7 @@ module WorkOS
|
|
|
24
24
|
def list_events(
|
|
25
25
|
before: nil,
|
|
26
26
|
after: nil,
|
|
27
|
-
limit:
|
|
27
|
+
limit: 10,
|
|
28
28
|
order: "desc",
|
|
29
29
|
events: nil,
|
|
30
30
|
range_start: nil,
|
data/lib/workos/feature_flags.rb
CHANGED
|
@@ -14,13 +14,13 @@ module WorkOS
|
|
|
14
14
|
# @param before [String, nil] An object ID that defines your place in the list. When the ID is not present, you are at the end of the list.
|
|
15
15
|
# @param after [String, nil] An object ID that defines your place in the list. When the ID is not present, you are at the end of the list.
|
|
16
16
|
# @param limit [Integer, nil] Upper limit on the number of objects to return, between `1` and `100`.
|
|
17
|
-
# @param order [WorkOS::Types::
|
|
17
|
+
# @param order [WorkOS::Types::PaginationOrder, nil] Order the results by the creation time.
|
|
18
18
|
# @param request_options [Hash] (see WorkOS::Types::RequestOptions)
|
|
19
19
|
# @return [WorkOS::Types::ListStruct<WorkOS::Flag>]
|
|
20
20
|
def list_feature_flags(
|
|
21
21
|
before: nil,
|
|
22
22
|
after: nil,
|
|
23
|
-
limit:
|
|
23
|
+
limit: 10,
|
|
24
24
|
order: "desc",
|
|
25
25
|
request_options: {}
|
|
26
26
|
)
|
|
@@ -154,14 +154,14 @@ module WorkOS
|
|
|
154
154
|
# @param before [String, nil] An object ID that defines your place in the list. When the ID is not present, you are at the end of the list.
|
|
155
155
|
# @param after [String, nil] An object ID that defines your place in the list. When the ID is not present, you are at the end of the list.
|
|
156
156
|
# @param limit [Integer, nil] Upper limit on the number of objects to return, between `1` and `100`.
|
|
157
|
-
# @param order [WorkOS::Types::
|
|
157
|
+
# @param order [WorkOS::Types::PaginationOrder, nil] Order the results by the creation time.
|
|
158
158
|
# @param request_options [Hash] (see WorkOS::Types::RequestOptions)
|
|
159
159
|
# @return [WorkOS::Types::ListStruct<WorkOS::Flag>]
|
|
160
160
|
def list_organization_feature_flags(
|
|
161
161
|
organization_id:,
|
|
162
162
|
before: nil,
|
|
163
163
|
after: nil,
|
|
164
|
-
limit:
|
|
164
|
+
limit: 10,
|
|
165
165
|
order: "desc",
|
|
166
166
|
request_options: {}
|
|
167
167
|
)
|
|
@@ -201,14 +201,14 @@ module WorkOS
|
|
|
201
201
|
# @param before [String, nil] An object ID that defines your place in the list. When the ID is not present, you are at the end of the list.
|
|
202
202
|
# @param after [String, nil] An object ID that defines your place in the list. When the ID is not present, you are at the end of the list.
|
|
203
203
|
# @param limit [Integer, nil] Upper limit on the number of objects to return, between `1` and `100`.
|
|
204
|
-
# @param order [WorkOS::Types::
|
|
204
|
+
# @param order [WorkOS::Types::PaginationOrder, nil] Order the results by the creation time.
|
|
205
205
|
# @param request_options [Hash] (see WorkOS::Types::RequestOptions)
|
|
206
206
|
# @return [WorkOS::Types::ListStruct<WorkOS::Flag>]
|
|
207
207
|
def list_user_feature_flags(
|
|
208
208
|
user_id:,
|
|
209
209
|
before: nil,
|
|
210
210
|
after: nil,
|
|
211
|
-
limit:
|
|
211
|
+
limit: 10,
|
|
212
212
|
order: "desc",
|
|
213
213
|
request_options: {}
|
|
214
214
|
)
|
data/lib/workos/groups.rb
CHANGED
|
@@ -15,14 +15,14 @@ module WorkOS
|
|
|
15
15
|
# @param before [String, nil] An object ID that defines your place in the list. When the ID is not present, you are at the end of the list. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`.
|
|
16
16
|
# @param after [String, nil] An object ID that defines your place in the list. When the ID is not present, you are at the end of the list. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`.
|
|
17
17
|
# @param limit [Integer, nil] Upper limit on the number of objects to return, between `1` and `100`.
|
|
18
|
-
# @param order [WorkOS::Types::
|
|
18
|
+
# @param order [WorkOS::Types::PaginationOrder, nil] Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending.
|
|
19
19
|
# @param request_options [Hash] (see WorkOS::Types::RequestOptions)
|
|
20
20
|
# @return [WorkOS::Types::ListStruct<WorkOS::Group>]
|
|
21
21
|
def list_organization_groups(
|
|
22
22
|
organization_id:,
|
|
23
23
|
before: nil,
|
|
24
24
|
after: nil,
|
|
25
|
-
limit:
|
|
25
|
+
limit: 10,
|
|
26
26
|
order: "desc",
|
|
27
27
|
request_options: {}
|
|
28
28
|
)
|
|
@@ -161,7 +161,7 @@ module WorkOS
|
|
|
161
161
|
# @param before [String, nil] An object ID that defines your place in the list. When the ID is not present, you are at the end of the list. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`.
|
|
162
162
|
# @param after [String, nil] An object ID that defines your place in the list. When the ID is not present, you are at the end of the list. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`.
|
|
163
163
|
# @param limit [Integer, nil] Upper limit on the number of objects to return, between `1` and `100`.
|
|
164
|
-
# @param order [WorkOS::Types::
|
|
164
|
+
# @param order [WorkOS::Types::PaginationOrder, nil] Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending.
|
|
165
165
|
# @param request_options [Hash] (see WorkOS::Types::RequestOptions)
|
|
166
166
|
# @return [WorkOS::Types::ListStruct<WorkOS::UserOrganizationMembershipBaseListData>]
|
|
167
167
|
def list_group_organization_memberships(
|
|
@@ -169,7 +169,7 @@ module WorkOS
|
|
|
169
169
|
group_id:,
|
|
170
170
|
before: nil,
|
|
171
171
|
after: nil,
|
|
172
|
-
limit:
|
|
172
|
+
limit: 10,
|
|
173
173
|
order: "desc",
|
|
174
174
|
request_options: {}
|
|
175
175
|
)
|
|
@@ -136,14 +136,14 @@ module WorkOS
|
|
|
136
136
|
# @param before [String, nil] An object ID that defines your place in the list. When the ID is not present, you are at the end of the list.
|
|
137
137
|
# @param after [String, nil] An object ID that defines your place in the list. When the ID is not present, you are at the end of the list.
|
|
138
138
|
# @param limit [Integer, nil] Upper limit on the number of objects to return, between `1` and `100`.
|
|
139
|
-
# @param order [WorkOS::Types::
|
|
139
|
+
# @param order [WorkOS::Types::PaginationOrder, nil] Order the results by the creation time.
|
|
140
140
|
# @param request_options [Hash] (see WorkOS::Types::RequestOptions)
|
|
141
141
|
# @return [WorkOS::Types::ListStruct<WorkOS::AuthenticationFactor>]
|
|
142
142
|
def list_user_auth_factors(
|
|
143
143
|
userland_user_id:,
|
|
144
144
|
before: nil,
|
|
145
145
|
after: nil,
|
|
146
|
-
limit:
|
|
146
|
+
limit: 10,
|
|
147
147
|
order: "desc",
|
|
148
148
|
request_options: {}
|
|
149
149
|
)
|
data/lib/workos/organizations.rb
CHANGED
|
@@ -14,7 +14,7 @@ module WorkOS
|
|
|
14
14
|
# @param before [String, nil] An object ID that defines your place in the list. When the ID is not present, you are at the end of the list. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`.
|
|
15
15
|
# @param after [String, nil] An object ID that defines your place in the list. When the ID is not present, you are at the end of the list. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`.
|
|
16
16
|
# @param limit [Integer, nil] Upper limit on the number of objects to return, between `1` and `100`.
|
|
17
|
-
# @param order [WorkOS::Types::
|
|
17
|
+
# @param order [WorkOS::Types::PaginationOrder, nil] Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending.
|
|
18
18
|
# @param domains [Array<String>, nil] The domains of an Organization. Any Organization with a matching domain will be returned.
|
|
19
19
|
# @param search [String, nil] Searchable text for an Organization. Matches against the organization name.
|
|
20
20
|
# @param request_options [Hash] (see WorkOS::Types::RequestOptions)
|
|
@@ -22,7 +22,7 @@ module WorkOS
|
|
|
22
22
|
def list_organizations(
|
|
23
23
|
before: nil,
|
|
24
24
|
after: nil,
|
|
25
|
-
limit:
|
|
25
|
+
limit: 10,
|
|
26
26
|
order: "desc",
|
|
27
27
|
domains: nil,
|
|
28
28
|
search: nil,
|
data/lib/workos/session.rb
CHANGED
|
@@ -21,8 +21,16 @@ module WorkOS
|
|
|
21
21
|
# @example Build a logout URL
|
|
22
22
|
# url = session.get_logout_url(return_to: "https://app.example.com")
|
|
23
23
|
class Session
|
|
24
|
+
# Minimum cookie_password byte length. AES-256-GCM derives a 32-byte
|
|
25
|
+
# key from the password via SHA-256; a passphrase shorter than the
|
|
26
|
+
# output it derives to provides less than the full keyspace and makes
|
|
27
|
+
# offline brute-force feasible. Require callers to supply at least 32
|
|
28
|
+
# bytes of high-entropy secret. See README + V7_MIGRATION_GUIDE.md.
|
|
29
|
+
MIN_COOKIE_PASSWORD_BYTES = 32
|
|
30
|
+
|
|
24
31
|
def initialize(manager, seal_data:, cookie_password:)
|
|
25
32
|
raise ArgumentError, "cookie_password is required" if cookie_password.nil? || cookie_password.empty?
|
|
33
|
+
raise ArgumentError, "cookie_password must be at least #{MIN_COOKIE_PASSWORD_BYTES} bytes" if cookie_password.bytesize < MIN_COOKIE_PASSWORD_BYTES
|
|
26
34
|
@manager = manager
|
|
27
35
|
@client = manager.client
|
|
28
36
|
@seal_data = seal_data
|
|
@@ -57,7 +65,7 @@ module WorkOS
|
|
|
57
65
|
return SessionManager::AuthError.new(authenticated: false, reason: SessionManager::INVALID_JWT)
|
|
58
66
|
end
|
|
59
67
|
|
|
60
|
-
is_expired = decoded["exp"]
|
|
68
|
+
is_expired = decoded["exp"].nil? || decoded["exp"] < Time.now.to_i
|
|
61
69
|
|
|
62
70
|
SessionManager::AuthSuccess.new(
|
|
63
71
|
authenticated: !is_expired,
|
|
@@ -77,6 +85,11 @@ module WorkOS
|
|
|
77
85
|
|
|
78
86
|
def refresh(organization_id: nil, cookie_password: nil)
|
|
79
87
|
effective_password = cookie_password || @cookie_password
|
|
88
|
+
# Validate up front so a caller-supplied short password raises ArgumentError
|
|
89
|
+
# (matching Session#initialize) instead of being swallowed by the
|
|
90
|
+
# unseal_data rescue and surfacing as INVALID_SESSION_COOKIE.
|
|
91
|
+
raise ArgumentError, "cookie_password is required" if effective_password.nil? || effective_password.empty?
|
|
92
|
+
raise ArgumentError, "cookie_password must be at least #{MIN_COOKIE_PASSWORD_BYTES} bytes" if effective_password.bytesize < MIN_COOKIE_PASSWORD_BYTES
|
|
80
93
|
|
|
81
94
|
session = begin
|
|
82
95
|
@manager.unseal_data(@seal_data, effective_password)
|
|
@@ -105,17 +118,20 @@ module WorkOS
|
|
|
105
118
|
impersonator: auth_response["impersonator"]
|
|
106
119
|
)
|
|
107
120
|
|
|
108
|
-
#
|
|
109
|
-
#
|
|
110
|
-
|
|
111
|
-
|
|
121
|
+
# Persist the new seal/password BEFORE decoding the JWT, so a transient
|
|
122
|
+
# JWKS fetch error (or any decode failure on the freshly-minted token)
|
|
123
|
+
# leaves the Session with a usable sealed cookie that the caller can
|
|
124
|
+
# re-#authenticate against, rather than half-updated state.
|
|
112
125
|
@seal_data = sealed
|
|
113
126
|
@cookie_password = effective_password
|
|
127
|
+
|
|
128
|
+
decoded = @manager.decode_jwt(auth_response["access_token"])
|
|
129
|
+
|
|
114
130
|
SessionManager::RefreshSuccess.new(
|
|
115
131
|
authenticated: true,
|
|
116
132
|
sealed_session: sealed,
|
|
117
133
|
session_id: decoded["sid"],
|
|
118
|
-
organization_id: decoded["org_id"],
|
|
134
|
+
organization_id: auth_response["organization_id"] || decoded["org_id"],
|
|
119
135
|
role: decoded["role"],
|
|
120
136
|
roles: decoded["roles"],
|
|
121
137
|
permissions: decoded["permissions"],
|
|
@@ -127,7 +143,12 @@ module WorkOS
|
|
|
127
143
|
rescue WorkOS::AuthenticationError, WorkOS::InvalidRequestError => e
|
|
128
144
|
SessionManager::RefreshError.new(authenticated: false, reason: e.message)
|
|
129
145
|
rescue JWT::DecodeError => e
|
|
130
|
-
|
|
146
|
+
# The refresh token was already rotated server-side before decode failed,
|
|
147
|
+
# so @seal_data holds the freshly-minted cookie. Surface it on the error
|
|
148
|
+
# struct so the caller can write the rotated cookie back to the browser
|
|
149
|
+
# and recover on a subsequent #authenticate, rather than re-sending the
|
|
150
|
+
# now-revoked refresh token.
|
|
151
|
+
SessionManager::RefreshError.new(authenticated: false, reason: e.message, sealed_session: @seal_data)
|
|
131
152
|
end
|
|
132
153
|
|
|
133
154
|
# Build the WorkOS session-logout URL for the currently authenticated session.
|
|
@@ -107,7 +107,7 @@ module WorkOS
|
|
|
107
107
|
:roles, :permissions, :entitlements, :user, :impersonator, :feature_flags,
|
|
108
108
|
keyword_init: true
|
|
109
109
|
)
|
|
110
|
-
RefreshError = Struct.new(:authenticated, :reason, keyword_init: true)
|
|
110
|
+
RefreshError = Struct.new(:authenticated, :reason, :sealed_session, keyword_init: true)
|
|
111
111
|
|
|
112
112
|
# Failure reason constants
|
|
113
113
|
NO_SESSION_COOKIE_PROVIDED = "no_session_cookie_provided"
|
|
@@ -150,12 +150,14 @@ module WorkOS
|
|
|
150
150
|
# H06 — Raw seal: encrypt arbitrary data with a key string.
|
|
151
151
|
# Delegates to the configured encryptor (default: AES-256-GCM).
|
|
152
152
|
def seal_data(data, key)
|
|
153
|
+
validate_cookie_password!(key)
|
|
153
154
|
@encryptor.seal(data, key)
|
|
154
155
|
end
|
|
155
156
|
|
|
156
157
|
# H06 — Raw unseal: returns parsed JSON (Hash) or raw string if not JSON.
|
|
157
158
|
# Delegates to the configured encryptor (default: AES-256-GCM).
|
|
158
159
|
def unseal_data(sealed, key)
|
|
160
|
+
validate_cookie_password!(key)
|
|
159
161
|
@encryptor.unseal(sealed, key)
|
|
160
162
|
end
|
|
161
163
|
|
|
@@ -164,11 +166,20 @@ module WorkOS
|
|
|
164
166
|
payload = {"access_token" => access_token, "refresh_token" => refresh_token}
|
|
165
167
|
payload["user"] = user if user
|
|
166
168
|
payload["impersonator"] = impersonator if impersonator
|
|
169
|
+
# Delegates to seal_data, which calls validate_cookie_password!; no need
|
|
170
|
+
# to validate here too.
|
|
167
171
|
seal_data(payload, cookie_password)
|
|
168
172
|
end
|
|
169
173
|
|
|
170
174
|
# Verify an access-token JWT against the WorkOS JWKS for this client.
|
|
171
175
|
# Used by Session#authenticate; exposed publicly for advanced cases.
|
|
176
|
+
#
|
|
177
|
+
# NOTE on iss/aud/required_claims: this method intentionally does not
|
|
178
|
+
# enforce iss, aud, or required_claims. workos-node's `jose` call and
|
|
179
|
+
# workos-php's `isset($exp) && $exp < time()` accept exp-less tokens, and
|
|
180
|
+
# cross-SDK parity is required for the planned coordinated hardening of
|
|
181
|
+
# these claims. See commit 9ce069f for the rationale behind dropping the
|
|
182
|
+
# required_claims: ['exp'] tightening that was considered here.
|
|
172
183
|
def decode_jwt(access_token, verify_expiration: true)
|
|
173
184
|
jwks = fetch_jwks
|
|
174
185
|
JWT.decode(
|
|
@@ -182,6 +193,18 @@ module WorkOS
|
|
|
182
193
|
).first
|
|
183
194
|
end
|
|
184
195
|
|
|
196
|
+
private
|
|
197
|
+
|
|
198
|
+
# Validate a cookie_password is non-empty and at least the minimum
|
|
199
|
+
# byte length required by Session::MIN_COOKIE_PASSWORD_BYTES (32).
|
|
200
|
+
# Defense-in-depth — Session#initialize enforces the same invariant
|
|
201
|
+
# on the load path; this guards the inline #seal_data / #unseal_data
|
|
202
|
+
# entry points.
|
|
203
|
+
def validate_cookie_password!(key)
|
|
204
|
+
raise ArgumentError, "cookie_password is required" if key.nil? || key.empty?
|
|
205
|
+
raise ArgumentError, "cookie_password must be at least #{Session::MIN_COOKIE_PASSWORD_BYTES} bytes" if key.bytesize < Session::MIN_COOKIE_PASSWORD_BYTES
|
|
206
|
+
end
|
|
207
|
+
|
|
185
208
|
# Cached JWKS fetch (5-minute TTL, thread-safe).
|
|
186
209
|
def fetch_jwks(now: Time.now)
|
|
187
210
|
@jwks_mutex.synchronize do
|
data/lib/workos/sso/profile.rb
CHANGED
|
@@ -14,6 +14,7 @@ module WorkOS
|
|
|
14
14
|
email: :email,
|
|
15
15
|
first_name: :first_name,
|
|
16
16
|
last_name: :last_name,
|
|
17
|
+
name: :name,
|
|
17
18
|
role: :role,
|
|
18
19
|
roles: :roles,
|
|
19
20
|
groups: :groups,
|
|
@@ -31,6 +32,7 @@ module WorkOS
|
|
|
31
32
|
:email,
|
|
32
33
|
:first_name,
|
|
33
34
|
:last_name,
|
|
35
|
+
:name,
|
|
34
36
|
:role,
|
|
35
37
|
:roles,
|
|
36
38
|
:groups,
|
|
@@ -48,6 +50,7 @@ module WorkOS
|
|
|
48
50
|
@email = hash[:email]
|
|
49
51
|
@first_name = hash[:first_name]
|
|
50
52
|
@last_name = hash[:last_name]
|
|
53
|
+
@name = hash[:name]
|
|
51
54
|
@role = hash[:role] ? WorkOS::SlimRole.new(hash[:role]) : nil
|
|
52
55
|
@roles = (hash[:roles] || []).map { |item| item ? WorkOS::SlimRole.new(item) : nil }
|
|
53
56
|
@groups = hash[:groups] || []
|