workos 0.4.2 → 0.8.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/Gemfile.lock +8 -11
- data/README.md +7 -0
- data/lib/workos.rb +3 -0
- data/lib/workos/audit_trail.rb +2 -2
- data/lib/workos/client.rb +13 -7
- data/lib/workos/organization.rb +49 -0
- data/lib/workos/passwordless.rb +81 -0
- data/lib/workos/portal.rb +149 -0
- data/lib/workos/profile.rb +6 -1
- data/lib/workos/types.rb +4 -0
- data/lib/workos/types/intent_enum.rb +14 -0
- data/lib/workos/types/list_struct.rb +13 -0
- data/lib/workos/types/organization_struct.rb +14 -0
- data/lib/workos/types/passwordless_session_struct.rb +15 -0
- data/lib/workos/types/profile_struct.rb +1 -0
- data/lib/workos/version.rb +1 -1
- data/spec/lib/workos/passwordless_spec.rb +82 -0
- data/spec/lib/workos/portal_spec.rb +176 -0
- data/spec/lib/workos/sso_spec.rb +18 -0
- data/spec/support/fixtures/vcr_cassettes/organization/create.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/organization/create_invalid.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/organization/list.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/passwordless/create_session.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/passwordless/create_session_invalid.yml +73 -0
- data/spec/support/fixtures/vcr_cassettes/passwordless/send_session.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/passwordless/send_session_invalid.yml +73 -0
- data/spec/support/fixtures/vcr_cassettes/portal/generate_link.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/portal/generate_link_invalid.yml +72 -0
- data/spec/support/profile.txt +1 -1
- data/workos.gemspec +1 -1
- metadata +33 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4d0c25850478e9c956759f36703587fd5da039b17063b8bbe5471937aadc15cf
|
4
|
+
data.tar.gz: ab19b3a879da2a46560d2fa0dffeb4da85428ebec07ceeabe6f45f38059e6734
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1894c61c6804fa7089f7475461e402d1803bca62fcf728ee7c549bd318980602df6f7aa6641cbd9050ebcacfe4fd12c9e85c8c3e5e880eed80d7fa3ce8468327
|
7
|
+
data.tar.gz: ce0521b22be9055589b774fab6d726105c01cd86cab54ddaa0e22b99338175a62d27b11e7aa5783b76b4b2bb69514e619897fd2c048ebacb71a1a057920d554d
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
workos (0.
|
4
|
+
workos (0.8.1)
|
5
5
|
sorbet-runtime (~> 0.5)
|
6
6
|
|
7
7
|
GEM
|
@@ -10,17 +10,16 @@ GEM
|
|
10
10
|
addressable (2.7.0)
|
11
11
|
public_suffix (>= 2.0.2, < 5.0)
|
12
12
|
ast (2.4.0)
|
13
|
-
codecov (0.
|
13
|
+
codecov (0.2.8)
|
14
14
|
json
|
15
15
|
simplecov
|
16
|
-
url
|
17
16
|
crack (0.4.3)
|
18
17
|
safe_yaml (~> 1.0.0)
|
19
18
|
diff-lcs (1.3)
|
20
19
|
docile (1.3.2)
|
21
20
|
hashdiff (1.0.0)
|
22
21
|
jaro_winkler (1.5.4)
|
23
|
-
json (2.3.
|
22
|
+
json (2.3.1)
|
24
23
|
parallel (1.19.1)
|
25
24
|
parser (2.7.0.0)
|
26
25
|
ast (~> 2.4.0)
|
@@ -49,17 +48,15 @@ GEM
|
|
49
48
|
unicode-display_width (>= 1.4.0, < 1.7)
|
50
49
|
ruby-progressbar (1.10.1)
|
51
50
|
safe_yaml (1.0.5)
|
52
|
-
simplecov (0.
|
51
|
+
simplecov (0.19.0)
|
53
52
|
docile (~> 1.1)
|
54
|
-
|
55
|
-
|
56
|
-
simplecov-html (0.10.2)
|
53
|
+
simplecov-html (~> 0.11)
|
54
|
+
simplecov-html (0.12.2)
|
57
55
|
sorbet (0.5.5560)
|
58
56
|
sorbet-static (= 0.5.5560)
|
59
|
-
sorbet-runtime (0.5.
|
57
|
+
sorbet-runtime (0.5.5923)
|
60
58
|
sorbet-static (0.5.5560-universal-darwin-14)
|
61
59
|
unicode-display_width (1.6.0)
|
62
|
-
url (0.3.2)
|
63
60
|
vcr (5.0.0)
|
64
61
|
webmock (3.7.6)
|
65
62
|
addressable (>= 2.3.6)
|
@@ -72,7 +69,7 @@ PLATFORMS
|
|
72
69
|
|
73
70
|
DEPENDENCIES
|
74
71
|
bundler (>= 2.0.1)
|
75
|
-
codecov (~> 0.
|
72
|
+
codecov (~> 0.2.8)
|
76
73
|
rake
|
77
74
|
rspec (~> 3.9.0)
|
78
75
|
rubocop (~> 0.77)
|
data/README.md
CHANGED
@@ -157,6 +157,13 @@ This method will return an instance of a `WorkOS::Profile` with the following at
|
|
157
157
|
@connection_type="OktaSAML",
|
158
158
|
@last_name="Demo",
|
159
159
|
@idp_id="00u1klkowm8EGah2H357",
|
160
|
+
@raw_attributes={
|
161
|
+
:id=>"prof_01DRA1XNSJDZ19A31F183ECQW5",
|
162
|
+
:email=>"demo@workos-okta.com",
|
163
|
+
:first_name=>"WorkOS",
|
164
|
+
:last_name=>"Demo",
|
165
|
+
:idp_id=>"00u1klkowm8EGah2H357"
|
166
|
+
},
|
160
167
|
>
|
161
168
|
```
|
162
169
|
|
data/lib/workos.rb
CHANGED
@@ -31,6 +31,9 @@ module WorkOS
|
|
31
31
|
autoload :AuditTrail, 'workos/audit_trail'
|
32
32
|
autoload :Connection, 'workos/connection'
|
33
33
|
autoload :DirectorySync, 'workos/directory_sync'
|
34
|
+
autoload :Organization, 'workos/organization'
|
35
|
+
autoload :Passwordless, 'workos/passwordless'
|
36
|
+
autoload :Portal, 'workos/portal'
|
34
37
|
autoload :Profile, 'workos/profile'
|
35
38
|
autoload :SSO, 'workos/sso'
|
36
39
|
|
data/lib/workos/audit_trail.rb
CHANGED
@@ -83,8 +83,8 @@ module WorkOS
|
|
83
83
|
# event occurred at or after
|
84
84
|
# @option options [String] occurred_at_lt ISO-8601 datetime of when an
|
85
85
|
# event occurred before
|
86
|
-
# @option options [String] ISO-8601 datetime of when an
|
87
|
-
# or before
|
86
|
+
# @option options [String] occurred_at_lte ISO-8601 datetime of when an
|
87
|
+
# event occured at or before
|
88
88
|
# @option options [String] search Keyword search
|
89
89
|
#
|
90
90
|
# @return [Array<Hash>]
|
data/lib/workos/client.rb
CHANGED
@@ -81,8 +81,7 @@ module WorkOS
|
|
81
81
|
].join('; ')
|
82
82
|
end
|
83
83
|
|
84
|
-
|
85
|
-
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
84
|
+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity
|
86
85
|
sig { params(response: ::T.untyped).void }
|
87
86
|
def handle_error_response(response:)
|
88
87
|
http_status = response.code.to_i
|
@@ -108,11 +107,10 @@ module WorkOS
|
|
108
107
|
request_id: response['x-request-id'],
|
109
108
|
)
|
110
109
|
when 422
|
111
|
-
|
112
|
-
|
113
|
-
|
110
|
+
message = json['message']
|
111
|
+
errors = extract_error(json['errors']) if json['errors']
|
112
|
+
message += " (#{errors})" if errors
|
114
113
|
|
115
|
-
message = "#{json['message']} (#{errors})"
|
116
114
|
raise InvalidRequestError.new(
|
117
115
|
message: message,
|
118
116
|
http_status: http_status,
|
@@ -120,6 +118,14 @@ module WorkOS
|
|
120
118
|
)
|
121
119
|
end
|
122
120
|
end
|
123
|
-
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
|
121
|
+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
def extract_error(errors)
|
126
|
+
errors.map do |error|
|
127
|
+
"#{error['field']}: #{error['code']}"
|
128
|
+
end.join('; ')
|
129
|
+
end
|
124
130
|
end
|
125
131
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
module WorkOS
|
7
|
+
# The Organization class provides a lightweight wrapper around
|
8
|
+
# a WorkOS Organization resource. This class is not meant to be instantiated
|
9
|
+
# in user space, and is instantiated internally but exposed.
|
10
|
+
class Organization
|
11
|
+
extend T::Sig
|
12
|
+
|
13
|
+
attr_accessor :id, :domains, :name
|
14
|
+
|
15
|
+
sig { params(json: String).void }
|
16
|
+
def initialize(json)
|
17
|
+
raw = parse_json(json)
|
18
|
+
|
19
|
+
@id = T.let(raw.id, String)
|
20
|
+
@name = T.let(raw.name, String)
|
21
|
+
@domains = T.let(raw.domains, Array)
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_json(*)
|
25
|
+
{
|
26
|
+
id: id,
|
27
|
+
name: name,
|
28
|
+
domains: domains,
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
sig do
|
35
|
+
params(
|
36
|
+
json_string: String,
|
37
|
+
).returns(WorkOS::Types::OrganizationStruct)
|
38
|
+
end
|
39
|
+
def parse_json(json_string)
|
40
|
+
hash = JSON.parse(json_string, symbolize_names: true)
|
41
|
+
|
42
|
+
WorkOS::Types::OrganizationStruct.new(
|
43
|
+
id: hash[:id],
|
44
|
+
name: hash[:name],
|
45
|
+
domains: hash[:domains],
|
46
|
+
)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
require 'net/http'
|
5
|
+
|
6
|
+
module WorkOS
|
7
|
+
# The Passwordless module provides convenience methods for working with
|
8
|
+
# passwordless sessions including the WorkOS Magic Link. You'll need a valid
|
9
|
+
# API key.
|
10
|
+
#
|
11
|
+
# @see https://workos.com/docs/sso/configuring-magic-link
|
12
|
+
module Passwordless
|
13
|
+
class << self
|
14
|
+
extend T::Sig
|
15
|
+
include Base
|
16
|
+
include Client
|
17
|
+
|
18
|
+
# Create a Passwordless Session.
|
19
|
+
#
|
20
|
+
# @param [Hash] options A hash with options for the session
|
21
|
+
# @option options [String] email The email of the user to authenticate.
|
22
|
+
# @option options [String] state Optional parameter that the redirect URI
|
23
|
+
# received from WorkOS will contain. The state parameter can be used to
|
24
|
+
# encode arbitrary information to help restore application state between
|
25
|
+
# redirects.
|
26
|
+
# @option options [String] type The type of Passwordless Session to
|
27
|
+
# create. Currently, the only supported value is 'MagicLink'.
|
28
|
+
#
|
29
|
+
# @return Hash
|
30
|
+
sig do
|
31
|
+
params(
|
32
|
+
options: Hash,
|
33
|
+
).returns(WorkOS::Types::PasswordlessSessionStruct)
|
34
|
+
end
|
35
|
+
|
36
|
+
# rubocop:disable Metrics/MethodLength
|
37
|
+
def create_session(options)
|
38
|
+
response = execute_request(
|
39
|
+
request: post_request(
|
40
|
+
path: '/passwordless/sessions',
|
41
|
+
auth: true,
|
42
|
+
body: options,
|
43
|
+
),
|
44
|
+
)
|
45
|
+
|
46
|
+
hash = JSON.parse(response.body)
|
47
|
+
|
48
|
+
WorkOS::Types::PasswordlessSessionStruct.new(
|
49
|
+
id: hash['id'],
|
50
|
+
email: hash['email'],
|
51
|
+
expires_at: Date.parse(hash['expires_at']),
|
52
|
+
link: hash['link'],
|
53
|
+
)
|
54
|
+
end
|
55
|
+
# rubocop:enable Metrics/MethodLength
|
56
|
+
|
57
|
+
# Send a Passwordless Session via email.
|
58
|
+
#
|
59
|
+
# @param [String] session_id The unique identifier of the Passwordless
|
60
|
+
# Session to send an email for.
|
61
|
+
#
|
62
|
+
# @return Hash
|
63
|
+
sig do
|
64
|
+
params(
|
65
|
+
session_id: String,
|
66
|
+
).returns(T::Hash[String, T::Boolean])
|
67
|
+
end
|
68
|
+
|
69
|
+
def send_session(session_id)
|
70
|
+
response = execute_request(
|
71
|
+
request: post_request(
|
72
|
+
path: "/passwordless/sessions/#{session_id}/send",
|
73
|
+
auth: true,
|
74
|
+
),
|
75
|
+
)
|
76
|
+
|
77
|
+
JSON.parse(response.body)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
require 'net/http'
|
5
|
+
|
6
|
+
module WorkOS
|
7
|
+
# The Portal module provides resource methods for working with the Admin
|
8
|
+
# Portal product
|
9
|
+
module Portal
|
10
|
+
class << self
|
11
|
+
extend T::Sig
|
12
|
+
include Base
|
13
|
+
include Client
|
14
|
+
|
15
|
+
GENERATE_LINK_INTENTS = WorkOS::Types::Intent.values.map(&:serialize).
|
16
|
+
freeze
|
17
|
+
|
18
|
+
# Create an organization
|
19
|
+
#
|
20
|
+
# @param [Array<String>] domains List of domains that belong to the
|
21
|
+
# organization
|
22
|
+
# @param [String] name A unique, descriptive name for the organization
|
23
|
+
sig do
|
24
|
+
params(
|
25
|
+
domains: T::Array[String],
|
26
|
+
name: String,
|
27
|
+
).returns(WorkOS::Organization)
|
28
|
+
end
|
29
|
+
def create_organization(domains:, name:)
|
30
|
+
request = post_request(
|
31
|
+
auth: true,
|
32
|
+
body: { domains: domains, name: name },
|
33
|
+
path: '/organizations',
|
34
|
+
)
|
35
|
+
|
36
|
+
response = execute_request(request: request)
|
37
|
+
check_and_raise_organization_error(response: response)
|
38
|
+
|
39
|
+
WorkOS::Organization.new(response.body)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Generate a link to grant access to an organization's Admin Portal
|
43
|
+
#
|
44
|
+
# @param [String] intent The access scope for the generated Admin Portal
|
45
|
+
# link. Valid values are: ["sso"]
|
46
|
+
# @param [String] organization The ID of the organization the Admin
|
47
|
+
# Portal link will be generated for.
|
48
|
+
# @param [String] The URL that the end user will be redirected to upon
|
49
|
+
# exiting the generated Admin Portal. If none is provided, the default
|
50
|
+
# redirect link set in your WorkOS Dashboard will be used.
|
51
|
+
sig do
|
52
|
+
params(
|
53
|
+
intent: String,
|
54
|
+
organization: String,
|
55
|
+
return_url: T.nilable(String),
|
56
|
+
).returns(String)
|
57
|
+
end
|
58
|
+
# rubocop:disable Metrics/MethodLength
|
59
|
+
def generate_link(intent:, organization:, return_url: nil)
|
60
|
+
validate_intent(intent)
|
61
|
+
|
62
|
+
request = post_request(
|
63
|
+
auth: true,
|
64
|
+
body: {
|
65
|
+
intent: intent,
|
66
|
+
organization: organization,
|
67
|
+
return_url: return_url,
|
68
|
+
},
|
69
|
+
path: '/portal/generate_link',
|
70
|
+
)
|
71
|
+
|
72
|
+
response = execute_request(request: request)
|
73
|
+
|
74
|
+
JSON.parse(response.body)['link']
|
75
|
+
end
|
76
|
+
# rubocop:enable Metrics/MethodLength
|
77
|
+
|
78
|
+
# Retrieve a list of organizations that have connections configured
|
79
|
+
# within your WorkOS dashboard.
|
80
|
+
#
|
81
|
+
# @param [Array<String>] domains Filter organizations to only return those
|
82
|
+
# that are associated with the provided domains.
|
83
|
+
# @param [String] before A pagination argument used to request
|
84
|
+
# organizations before the provided Organization ID.
|
85
|
+
# @param [String] after A pagination argument used to request
|
86
|
+
# organizations after the provided Organization ID.
|
87
|
+
# @param [Integer] limit A pagination argument used to limit the number
|
88
|
+
# of listed Organizations that are returned.
|
89
|
+
sig do
|
90
|
+
params(
|
91
|
+
options: T::Hash[Symbol, String],
|
92
|
+
).returns(WorkOS::Types::ListStruct)
|
93
|
+
end
|
94
|
+
# rubocop:disable Metrics/MethodLength
|
95
|
+
def list_organizations(options = {})
|
96
|
+
response = execute_request(
|
97
|
+
request: get_request(
|
98
|
+
path: '/organizations',
|
99
|
+
auth: true,
|
100
|
+
params: options,
|
101
|
+
),
|
102
|
+
)
|
103
|
+
|
104
|
+
parsed_response = JSON.parse(response.body)
|
105
|
+
|
106
|
+
organizations = parsed_response['data'].map do |organization|
|
107
|
+
::WorkOS::Organization.new(organization.to_json)
|
108
|
+
end
|
109
|
+
|
110
|
+
WorkOS::Types::ListStruct.new(
|
111
|
+
data: organizations,
|
112
|
+
list_metadata: parsed_response['listMetadata'],
|
113
|
+
)
|
114
|
+
end
|
115
|
+
# rubocop:enable Metrics/MethodLength
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
sig { params(response: Net::HTTPResponse).void }
|
120
|
+
# rubocop:disable Metrics/MethodLength
|
121
|
+
def check_and_raise_organization_error(response:)
|
122
|
+
begin
|
123
|
+
body = JSON.parse(response.body)
|
124
|
+
return unless body['message']
|
125
|
+
|
126
|
+
message = body['message']
|
127
|
+
request_id = response['x-request-id']
|
128
|
+
rescue StandardError
|
129
|
+
message = 'Something went wrong'
|
130
|
+
end
|
131
|
+
|
132
|
+
raise APIError.new(
|
133
|
+
message: message,
|
134
|
+
http_status: nil,
|
135
|
+
request_id: request_id,
|
136
|
+
)
|
137
|
+
end
|
138
|
+
# rubocop:enable Metrics/MethodLength
|
139
|
+
|
140
|
+
sig { params(intent: String).void }
|
141
|
+
def validate_intent(intent)
|
142
|
+
return if GENERATE_LINK_INTENTS.include?(intent)
|
143
|
+
|
144
|
+
raise ArgumentError, "#{intent} is not a valid value." \
|
145
|
+
" `intent` must be in #{GENERATE_LINK_INTENTS}"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
data/lib/workos/profile.rb
CHANGED
@@ -14,7 +14,7 @@ module WorkOS
|
|
14
14
|
|
15
15
|
sig { returns(String) }
|
16
16
|
attr_accessor :id, :email, :first_name, :last_name,
|
17
|
-
:connection_type, :idp_id
|
17
|
+
:connection_type, :idp_id, :raw_attributes
|
18
18
|
|
19
19
|
sig { params(profile_json: String).void }
|
20
20
|
def initialize(profile_json)
|
@@ -26,6 +26,7 @@ module WorkOS
|
|
26
26
|
@last_name = raw.last_name
|
27
27
|
@connection_type = T.let(raw.connection_type, String)
|
28
28
|
@idp_id = raw.idp_id
|
29
|
+
@raw_attributes = raw.raw_attributes
|
29
30
|
end
|
30
31
|
|
31
32
|
sig { returns(String) }
|
@@ -41,11 +42,13 @@ module WorkOS
|
|
41
42
|
last_name: last_name,
|
42
43
|
connection_type: connection_type,
|
43
44
|
idp_id: idp_id,
|
45
|
+
raw_attributes: raw_attributes,
|
44
46
|
}
|
45
47
|
end
|
46
48
|
|
47
49
|
private
|
48
50
|
|
51
|
+
# rubocop:disable Metrics/AbcSize
|
49
52
|
sig { params(json_string: String).returns(WorkOS::Types::ProfileStruct) }
|
50
53
|
def parse_json(json_string)
|
51
54
|
hash = JSON.parse(json_string, symbolize_names: true)
|
@@ -57,7 +60,9 @@ module WorkOS
|
|
57
60
|
last_name: hash[:profile][:last_name],
|
58
61
|
connection_type: hash[:profile][:connection_type],
|
59
62
|
idp_id: hash[:profile][:idp_id],
|
63
|
+
raw_attributes: hash[:profile][:raw_attributes],
|
60
64
|
)
|
61
65
|
end
|
66
|
+
# rubocop:enable Metrics/AbcSize
|
62
67
|
end
|
63
68
|
end
|