workos 5.3.0 → 6.1.0
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/CODEOWNERS +1 -1
- data/.github/workflows/ci.yml +2 -4
- data/.github/workflows/lint-pr-title.yml +20 -0
- data/.github/workflows/release-please.yml +25 -0
- data/.github/workflows/release.yml +22 -25
- data/.gitignore +1 -0
- data/.release-please-manifest.json +3 -0
- data/.rubocop.yml +11 -8
- data/.rubocop_todo.yml +94 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +15 -0
- data/Gemfile.lock +32 -18
- data/Rakefile +8 -0
- data/context7.json +4 -0
- data/lib/workos/authentication_response.rb +32 -4
- data/lib/workos/cache.rb +94 -0
- data/lib/workos/client.rb +9 -1
- data/lib/workos/directory_sync.rb +1 -1
- data/lib/workos/directory_user.rb +31 -3
- data/lib/workos/encryptors/aes_gcm.rb +49 -0
- data/lib/workos/encryptors.rb +9 -0
- data/lib/workos/errors.rb +4 -0
- data/lib/workos/feature_flag.rb +34 -0
- data/lib/workos/mfa.rb +0 -1
- data/lib/workos/oauth_tokens.rb +29 -0
- data/lib/workos/organization.rb +14 -1
- data/lib/workos/organization_membership.rb +5 -1
- data/lib/workos/organizations.rb +87 -3
- data/lib/workos/profile.rb +10 -2
- data/lib/workos/refresh_authentication_response.rb +29 -2
- data/lib/workos/role.rb +38 -0
- data/lib/workos/session.rb +187 -0
- data/lib/workos/sso.rb +3 -24
- data/lib/workos/types/intent.rb +3 -1
- data/lib/workos/types/provider.rb +1 -1
- data/lib/workos/types/widget_scope.rb +15 -0
- data/lib/workos/types.rb +1 -0
- data/lib/workos/user.rb +7 -1
- data/lib/workos/user_management/session.rb +57 -0
- data/lib/workos/user_management.rb +213 -45
- data/lib/workos/version.rb +1 -1
- data/lib/workos/widgets.rb +46 -0
- data/lib/workos.rb +8 -0
- data/release-please-config.json +12 -0
- data/spec/lib/workos/cache_spec.rb +94 -0
- data/spec/lib/workos/directory_user_spec.rb +13 -3
- data/spec/lib/workos/encryptors/aes_gcm_spec.rb +41 -0
- data/spec/lib/workos/organizations_spec.rb +258 -1
- data/spec/lib/workos/portal_spec.rb +30 -0
- data/spec/lib/workos/role_spec.rb +142 -0
- data/spec/lib/workos/session_spec.rb +475 -0
- data/spec/lib/workos/sso_spec.rb +106 -5
- data/spec/lib/workos/user_management_spec.rb +496 -1
- data/spec/lib/workos/widgets_spec.rb +73 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/get_user.yml +1 -1
- data/spec/support/fixtures/vcr_cassettes/organization/create_with_external_id.yml +83 -0
- data/spec/support/fixtures/vcr_cassettes/organization/list_organization_feature_flags.yml +78 -0
- data/spec/support/fixtures/vcr_cassettes/organization/list_organization_roles.yml +82 -0
- data/spec/support/fixtures/vcr_cassettes/organization/update_with_external_id.yml +78 -0
- data/spec/support/fixtures/vcr_cassettes/organization/update_with_external_id_null.yml +78 -0
- data/spec/support/fixtures/vcr_cassettes/organization/update_with_stripe_customer_id.yml +78 -0
- data/spec/support/fixtures/vcr_cassettes/organization/update_without_name.yml +85 -0
- data/spec/support/fixtures/vcr_cassettes/portal/generate_link_certificate_renewal.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/portal/generate_link_domain_verification.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/sso/profile.yml +1 -1
- data/spec/support/fixtures/vcr_cassettes/user_management/authenticate_with_code/valid_with_oauth_tokens.yml +82 -0
- data/spec/support/fixtures/vcr_cassettes/user_management/authenticate_with_password/unverified.yml +82 -0
- data/spec/support/fixtures/vcr_cassettes/user_management/authenticate_with_refresh_token/valid.yml +79 -78
- data/spec/support/fixtures/vcr_cassettes/user_management/create_organization_membership/valid_multiple_roles.yml +76 -0
- data/spec/support/fixtures/vcr_cassettes/user_management/create_user_with_external_id.yml +77 -0
- data/spec/support/fixtures/vcr_cassettes/user_management/get_user.yml +1 -1
- data/spec/support/fixtures/vcr_cassettes/user_management/list_sessions/valid.yml +38 -0
- data/spec/support/fixtures/vcr_cassettes/user_management/resend_invitation/accepted.yml +83 -0
- data/spec/support/fixtures/vcr_cassettes/user_management/resend_invitation/expired.yml +83 -0
- data/spec/support/fixtures/vcr_cassettes/user_management/resend_invitation/invalid.yml +83 -0
- data/spec/support/fixtures/vcr_cassettes/user_management/resend_invitation/revoked.yml +83 -0
- data/spec/support/fixtures/vcr_cassettes/user_management/resend_invitation/valid.yml +83 -0
- data/spec/support/fixtures/vcr_cassettes/user_management/reset_password/valid.yml +1 -1
- data/spec/support/fixtures/vcr_cassettes/user_management/update_organization_membership/valid_multiple_roles.yml +76 -0
- data/spec/support/fixtures/vcr_cassettes/user_management/update_user/email.yml +82 -0
- data/spec/support/fixtures/vcr_cassettes/user_management/update_user/locale.yml +76 -0
- data/spec/support/fixtures/vcr_cassettes/user_management/update_user/valid.yml +2 -2
- data/spec/support/fixtures/vcr_cassettes/user_management/update_user_external_id_null.yml +77 -0
- data/spec/support/fixtures/vcr_cassettes/widgets/get_token.yml +82 -0
- data/spec/support/fixtures/vcr_cassettes/widgets/get_token_invalid_organization_id.yml +74 -0
- data/spec/support/fixtures/vcr_cassettes/widgets/get_token_invalid_user_id.yml +74 -0
- data/spec/support/profile.txt +1 -1
- data/workos.gemspec +7 -3
- metadata +132 -10
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'jwt'
|
|
4
|
+
require 'uri'
|
|
5
|
+
require 'net/http'
|
|
6
|
+
require 'encryptor'
|
|
7
|
+
require 'securerandom'
|
|
8
|
+
require 'json'
|
|
9
|
+
require 'uri'
|
|
10
|
+
|
|
11
|
+
module WorkOS
|
|
12
|
+
# The Session class provides helper methods for working with WorkOS sessions
|
|
13
|
+
# This class is not meant to be instantiated in a user space, and is instantiated internally but exposed.
|
|
14
|
+
class Session
|
|
15
|
+
attr_accessor :jwks, :jwks_algorithms, :user_management, :cookie_password, :session_data, :client_id, :encryptor
|
|
16
|
+
|
|
17
|
+
def initialize(user_management:, client_id:, session_data:, cookie_password:, encryptor: nil)
|
|
18
|
+
raise ArgumentError, 'cookiePassword is required' if cookie_password.nil? || cookie_password.empty?
|
|
19
|
+
|
|
20
|
+
@encryptor = encryptor || WorkOS::Encryptors::AesGcm.new
|
|
21
|
+
validate_encryptor!(@encryptor)
|
|
22
|
+
|
|
23
|
+
@user_management = user_management
|
|
24
|
+
@cookie_password = cookie_password
|
|
25
|
+
@session_data = session_data
|
|
26
|
+
@client_id = client_id
|
|
27
|
+
|
|
28
|
+
@jwks = Cache.fetch("jwks_#{client_id}", expires_in: 5 * 60) do
|
|
29
|
+
create_remote_jwk_set(URI(@user_management.get_jwks_url(client_id)))
|
|
30
|
+
end
|
|
31
|
+
@jwks_algorithms = @jwks.map { |key| key[:alg] }.compact.uniq
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Authenticates the user based on the session data
|
|
35
|
+
# @param include_expired [Boolean] If true, returns decoded token data even when expired (default: false)
|
|
36
|
+
# @param block [Proc] Optional block to call to extract additional claims from the decoded JWT
|
|
37
|
+
# @return [Hash] A hash containing the authentication response and a reason if the authentication failed
|
|
38
|
+
# rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
|
|
39
|
+
def authenticate(include_expired: false, &claim_extractor)
|
|
40
|
+
return { authenticated: false, reason: 'NO_SESSION_COOKIE_PROVIDED' } if @session_data.nil?
|
|
41
|
+
|
|
42
|
+
begin
|
|
43
|
+
session = Session.unseal_data(@session_data, @cookie_password, encryptor: @encryptor)
|
|
44
|
+
rescue StandardError
|
|
45
|
+
return { authenticated: false, reason: 'INVALID_SESSION_COOKIE' }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
return { authenticated: false, reason: 'INVALID_SESSION_COOKIE' } unless session[:access_token]
|
|
49
|
+
|
|
50
|
+
begin
|
|
51
|
+
decoded = JWT.decode(
|
|
52
|
+
session[:access_token],
|
|
53
|
+
nil,
|
|
54
|
+
true,
|
|
55
|
+
algorithms: @jwks_algorithms,
|
|
56
|
+
jwks: @jwks,
|
|
57
|
+
verify_expiration: false,
|
|
58
|
+
).first
|
|
59
|
+
|
|
60
|
+
expired = decoded['exp'] && decoded['exp'] < Time.now.to_i
|
|
61
|
+
|
|
62
|
+
# Early return for expired tokens when not including expired data (backward compatible)
|
|
63
|
+
return { authenticated: false, reason: 'INVALID_JWT' } if expired && !include_expired
|
|
64
|
+
|
|
65
|
+
# Return full data for valid tokens or when include_expired is true
|
|
66
|
+
result = {
|
|
67
|
+
authenticated: !expired,
|
|
68
|
+
session_id: decoded['sid'],
|
|
69
|
+
organization_id: decoded['org_id'],
|
|
70
|
+
role: decoded['role'],
|
|
71
|
+
roles: decoded['roles'],
|
|
72
|
+
permissions: decoded['permissions'],
|
|
73
|
+
entitlements: decoded['entitlements'],
|
|
74
|
+
feature_flags: decoded['feature_flags'],
|
|
75
|
+
user: session[:user],
|
|
76
|
+
impersonator: session[:impersonator],
|
|
77
|
+
reason: expired ? 'INVALID_JWT' : nil,
|
|
78
|
+
}
|
|
79
|
+
result.merge!(claim_extractor.call(decoded)) if block_given?
|
|
80
|
+
result
|
|
81
|
+
rescue JWT::DecodeError
|
|
82
|
+
{ authenticated: false, reason: 'INVALID_JWT' }
|
|
83
|
+
rescue StandardError => e
|
|
84
|
+
{ authenticated: false, reason: e.message }
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Refreshes the session data using the refresh token stored in the session data
|
|
89
|
+
# @param options [Hash] Options for refreshing the session
|
|
90
|
+
# @option options [String] :cookie_password The password to use for unsealing the session data
|
|
91
|
+
# @option options [String] :organization_id The organization ID to use for refreshing the session
|
|
92
|
+
# @return [Hash] A hash containing a new sealed session, the authentication response,
|
|
93
|
+
# and a reason if the refresh failed
|
|
94
|
+
def refresh(options = nil)
|
|
95
|
+
cookie_password = options.nil? || options[:cookie_password].nil? ? @cookie_password : options[:cookie_password]
|
|
96
|
+
|
|
97
|
+
begin
|
|
98
|
+
session = Session.unseal_data(@session_data, cookie_password, encryptor: @encryptor)
|
|
99
|
+
rescue StandardError
|
|
100
|
+
return { authenticated: false, reason: 'INVALID_SESSION_COOKIE' }
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
return { authenticated: false, reason: 'INVALID_SESSION_COOKIE' } unless session[:refresh_token] && session[:user]
|
|
104
|
+
|
|
105
|
+
begin
|
|
106
|
+
auth_response = @user_management.authenticate_with_refresh_token(
|
|
107
|
+
client_id: @client_id,
|
|
108
|
+
refresh_token: session[:refresh_token],
|
|
109
|
+
organization_id: options.nil? || options[:organization_id].nil? ? nil : options[:organization_id],
|
|
110
|
+
session: { seal_session: true, cookie_password: cookie_password, encryptor: @encryptor },
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
@session_data = auth_response.sealed_session
|
|
114
|
+
@cookie_password = cookie_password
|
|
115
|
+
|
|
116
|
+
{
|
|
117
|
+
authenticated: true,
|
|
118
|
+
sealed_session: auth_response.sealed_session,
|
|
119
|
+
session: auth_response,
|
|
120
|
+
reason: nil,
|
|
121
|
+
}
|
|
122
|
+
rescue StandardError => e
|
|
123
|
+
{ authenticated: false, reason: e.message }
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
# rubocop:enable Metrics/AbcSize
|
|
127
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
|
128
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
|
129
|
+
|
|
130
|
+
# Returns a URL to redirect the user to for logging out
|
|
131
|
+
# @param return_to [String] The URL to redirect the user to after logging out
|
|
132
|
+
# @return [String] The URL to redirect the user to for logging out
|
|
133
|
+
def get_logout_url(return_to: nil)
|
|
134
|
+
auth_response = authenticate
|
|
135
|
+
|
|
136
|
+
unless auth_response[:authenticated]
|
|
137
|
+
raise "Failed to extract session ID for logout URL: #{auth_response[:reason]}"
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
@user_management.get_logout_url(session_id: auth_response[:session_id], return_to: return_to)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Encrypts and seals data using the provided encryptor (defaults to AES-256-GCM)
|
|
144
|
+
# @param data [Hash] The data to seal
|
|
145
|
+
# @param key [String] The key to use for encryption
|
|
146
|
+
# @param encryptor [Object] Optional encryptor that responds to #seal(data, key)
|
|
147
|
+
# @return [String] The sealed data
|
|
148
|
+
def self.seal_data(data, key, encryptor: nil)
|
|
149
|
+
enc = encryptor || WorkOS::Encryptors::AesGcm.new
|
|
150
|
+
enc.seal(data, key)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Decrypts and unseals data using the provided encryptor (defaults to AES-256-GCM)
|
|
154
|
+
# @param sealed_data [String] The sealed data to unseal
|
|
155
|
+
# @param key [String] The key to use for decryption
|
|
156
|
+
# @param encryptor [Object] Optional encryptor that responds to #unseal(sealed_data, key)
|
|
157
|
+
# @return [Hash] The unsealed data
|
|
158
|
+
def self.unseal_data(sealed_data, key, encryptor: nil)
|
|
159
|
+
enc = encryptor || WorkOS::Encryptors::AesGcm.new
|
|
160
|
+
enc.unseal(sealed_data, key)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
private
|
|
164
|
+
|
|
165
|
+
def validate_encryptor!(enc)
|
|
166
|
+
return if enc.respond_to?(:seal) && enc.respond_to?(:unseal)
|
|
167
|
+
|
|
168
|
+
raise ArgumentError, 'encryptor must respond to #seal(data, key) and #unseal(sealed_data, key)'
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Creates a JWKS set from a remote JWKS URL
|
|
172
|
+
# @param uri [URI] The URI of the JWKS
|
|
173
|
+
# @return [JWT::JWK::Set] The JWKS set
|
|
174
|
+
def create_remote_jwk_set(uri)
|
|
175
|
+
# Fetch the JWKS from the remote URL
|
|
176
|
+
response = Net::HTTP.get(uri)
|
|
177
|
+
|
|
178
|
+
jwks_hash = JSON.parse(response)
|
|
179
|
+
jwks = JWT::JWK::Set.new(jwks_hash)
|
|
180
|
+
|
|
181
|
+
# filter jwks so it only returns the keys where 'use' is equal to 'sig'
|
|
182
|
+
jwks.keys.select! { |key| key[:use] == 'sig' }
|
|
183
|
+
|
|
184
|
+
jwks
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
data/lib/workos/sso.rb
CHANGED
|
@@ -120,8 +120,9 @@ module WorkOS
|
|
|
120
120
|
code: code,
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
response =
|
|
124
|
-
|
|
123
|
+
response = execute_request(
|
|
124
|
+
request: post_request(path: '/sso/token', body: body),
|
|
125
|
+
)
|
|
125
126
|
|
|
126
127
|
WorkOS::ProfileAndToken.new(response.body)
|
|
127
128
|
end
|
|
@@ -229,28 +230,6 @@ module WorkOS
|
|
|
229
230
|
raise ArgumentError, "#{provider} is not a valid value." \
|
|
230
231
|
" `provider` must be in #{PROVIDERS}"
|
|
231
232
|
end
|
|
232
|
-
|
|
233
|
-
def check_and_raise_profile_and_token_error(response:)
|
|
234
|
-
begin
|
|
235
|
-
body = JSON.parse(response.body)
|
|
236
|
-
return if body['access_token'] && body['profile']
|
|
237
|
-
|
|
238
|
-
message = body['message']
|
|
239
|
-
error = body['error']
|
|
240
|
-
error_description = body['error_description']
|
|
241
|
-
request_id = response['x-request-id']
|
|
242
|
-
rescue StandardError
|
|
243
|
-
message = 'Something went wrong'
|
|
244
|
-
end
|
|
245
|
-
|
|
246
|
-
raise APIError.new(
|
|
247
|
-
message: message,
|
|
248
|
-
error: error,
|
|
249
|
-
error_description: error_description,
|
|
250
|
-
http_status: nil,
|
|
251
|
-
request_id: request_id,
|
|
252
|
-
)
|
|
253
|
-
end
|
|
254
233
|
end
|
|
255
234
|
end
|
|
256
235
|
end
|
data/lib/workos/types/intent.rb
CHANGED
|
@@ -6,11 +6,13 @@ module WorkOS
|
|
|
6
6
|
# intents while generating an Admin Portal link.
|
|
7
7
|
module Intent
|
|
8
8
|
AUDIT_LOGS = 'audit_logs'
|
|
9
|
+
CERTIFICATE_RENEWAL = 'certificate_renewal'
|
|
10
|
+
DOMAIN_VERIFICATION = 'domain_verification'
|
|
9
11
|
DSYNC = 'dsync'
|
|
10
12
|
LOG_STREAMS = 'log_streams'
|
|
11
13
|
SSO = 'sso'
|
|
12
14
|
|
|
13
|
-
ALL = [AUDIT_LOGS, DSYNC, LOG_STREAMS, SSO].freeze
|
|
15
|
+
ALL = [AUDIT_LOGS, CERTIFICATE_RENEWAL, DOMAIN_VERIFICATION, DSYNC, LOG_STREAMS, SSO].freeze
|
|
14
16
|
end
|
|
15
17
|
end
|
|
16
18
|
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WorkOS
|
|
4
|
+
module Types
|
|
5
|
+
# The WidgetScope constants are declarations of a fixed set of values for
|
|
6
|
+
# scopes while generating a widget token.
|
|
7
|
+
module WidgetScope
|
|
8
|
+
USERS_TABLE_MANAGE = 'widgets:users-table:manage'
|
|
9
|
+
SSO_MANAGE = 'widgets:sso:manage'
|
|
10
|
+
DOMAIN_VERIFICATION_MANAGE = 'widgets:domain-verification:manage'
|
|
11
|
+
|
|
12
|
+
ALL = [USERS_TABLE_MANAGE, SSO_MANAGE, DOMAIN_VERIFICATION_MANAGE].freeze
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
data/lib/workos/types.rb
CHANGED
data/lib/workos/user.rb
CHANGED
|
@@ -8,7 +8,7 @@ module WorkOS
|
|
|
8
8
|
include HashProvider
|
|
9
9
|
|
|
10
10
|
attr_accessor :id, :email, :first_name, :last_name, :email_verified,
|
|
11
|
-
:profile_picture_url, :created_at, :updated_at
|
|
11
|
+
:profile_picture_url, :external_id, :locale, :last_sign_in_at, :created_at, :updated_at
|
|
12
12
|
|
|
13
13
|
def initialize(json)
|
|
14
14
|
hash = JSON.parse(json, symbolize_names: true)
|
|
@@ -19,6 +19,9 @@ module WorkOS
|
|
|
19
19
|
@last_name = hash[:last_name]
|
|
20
20
|
@email_verified = hash[:email_verified]
|
|
21
21
|
@profile_picture_url = hash[:profile_picture_url]
|
|
22
|
+
@external_id = hash[:external_id]
|
|
23
|
+
@locale = hash[:locale]
|
|
24
|
+
@last_sign_in_at = hash[:last_sign_in_at]
|
|
22
25
|
@created_at = hash[:created_at]
|
|
23
26
|
@updated_at = hash[:updated_at]
|
|
24
27
|
end
|
|
@@ -31,6 +34,9 @@ module WorkOS
|
|
|
31
34
|
last_name: last_name,
|
|
32
35
|
email_verified: email_verified,
|
|
33
36
|
profile_picture_url: profile_picture_url,
|
|
37
|
+
external_id: external_id,
|
|
38
|
+
locale: locale,
|
|
39
|
+
last_sign_in_at: last_sign_in_at,
|
|
34
40
|
created_at: created_at,
|
|
35
41
|
updated_at: updated_at,
|
|
36
42
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WorkOS
|
|
4
|
+
module UserManagement
|
|
5
|
+
# The Session class provides a lightweight wrapper around
|
|
6
|
+
# a WorkOS Session resource. This class is not meant to be instantiated
|
|
7
|
+
# in user space, and is instantiated internally but exposed.
|
|
8
|
+
class Session
|
|
9
|
+
include HashProvider
|
|
10
|
+
attr_accessor :id, :object, :user_id, :organization_id, :status, :auth_method,
|
|
11
|
+
:ip_address, :user_agent, :expires_at, :ended_at, :created_at, :updated_at
|
|
12
|
+
|
|
13
|
+
# rubocop:disable Metrics/AbcSize
|
|
14
|
+
def initialize(json)
|
|
15
|
+
hash = JSON.parse(json, symbolize_names: true)
|
|
16
|
+
|
|
17
|
+
@id = hash[:id]
|
|
18
|
+
@object = hash[:object]
|
|
19
|
+
@user_id = hash[:user_id]
|
|
20
|
+
@organization_id = hash[:organization_id]
|
|
21
|
+
@status = hash[:status]
|
|
22
|
+
@auth_method = hash[:auth_method]
|
|
23
|
+
@ip_address = hash[:ip_address]
|
|
24
|
+
@user_agent = hash[:user_agent]
|
|
25
|
+
@expires_at = hash[:expires_at]
|
|
26
|
+
@ended_at = hash[:ended_at]
|
|
27
|
+
@created_at = hash[:created_at]
|
|
28
|
+
@updated_at = hash[:updated_at]
|
|
29
|
+
end
|
|
30
|
+
# rubocop:enable Metrics/AbcSize
|
|
31
|
+
|
|
32
|
+
def to_json(*)
|
|
33
|
+
{
|
|
34
|
+
id: id,
|
|
35
|
+
object: object,
|
|
36
|
+
user_id: user_id,
|
|
37
|
+
organization_id: organization_id,
|
|
38
|
+
status: status,
|
|
39
|
+
auth_method: auth_method,
|
|
40
|
+
ip_address: ip_address,
|
|
41
|
+
user_agent: user_agent,
|
|
42
|
+
expires_at: expires_at,
|
|
43
|
+
ended_at: ended_at,
|
|
44
|
+
created_at: created_at,
|
|
45
|
+
updated_at: updated_at,
|
|
46
|
+
}
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Revoke this session
|
|
50
|
+
#
|
|
51
|
+
# @return [Bool] - returns `true` if successful
|
|
52
|
+
def revoke
|
|
53
|
+
WorkOS::UserManagement.revoke_session(session_id: id)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|