workos 0.9.0 → 1.0.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 +5 -0
- data/.rubocop.yml +5 -1
- data/.ruby-version +1 -1
- data/.semaphore/semaphore.yml +8 -2
- data/Gemfile.lock +49 -36
- data/LICENSE +1 -1
- data/README.md +13 -167
- data/docs/WorkOS/SSO.html +235 -235
- data/docs/file.README.html +20 -20
- data/lib/workos/audit_trail.rb +1 -0
- data/lib/workos/client.rb +42 -4
- data/lib/workos/connection.rb +12 -3
- data/lib/workos/directory.rb +53 -0
- data/lib/workos/directory_group.rb +44 -0
- data/lib/workos/directory_sync.rb +63 -7
- data/lib/workos/directory_user.rb +63 -0
- data/lib/workos/organization.rb +0 -2
- data/lib/workos/organizations.rb +150 -0
- data/lib/workos/passwordless.rb +7 -2
- data/lib/workos/portal.rb +1 -87
- data/lib/workos/profile.rb +3 -6
- data/lib/workos/profile_and_token.rb +28 -0
- data/lib/workos/sso.rb +106 -65
- data/lib/workos/types/connection_struct.rb +3 -0
- data/lib/workos/types/directory_group_struct.rb +13 -0
- data/lib/workos/types/directory_struct.rb +16 -0
- data/lib/workos/types/directory_user_struct.rb +19 -0
- data/lib/workos/types/intent_enum.rb +1 -0
- data/lib/workos/types.rb +3 -0
- data/lib/workos/version.rb +1 -1
- data/lib/workos.rb +6 -0
- data/sorbet/rbi/gems/addressable.rbi +199 -0
- data/sorbet/rbi/gems/ast.rbi +49 -0
- data/sorbet/rbi/gems/codecov.rbi +37 -0
- data/sorbet/rbi/gems/crack.rbi +62 -0
- data/sorbet/rbi/gems/docile.rbi +36 -0
- data/sorbet/rbi/gems/hashdiff.rbi +66 -0
- data/sorbet/rbi/gems/parallel.rbi +83 -0
- data/sorbet/rbi/gems/parser.rbi +1429 -0
- data/sorbet/rbi/gems/public_suffix.rbi +104 -0
- data/sorbet/rbi/gems/rainbow.rbi +118 -0
- data/sorbet/rbi/gems/rake.rbi +644 -0
- data/sorbet/rbi/gems/regexp_parser.rbi +926 -0
- data/sorbet/rbi/gems/rexml.rbi +628 -0
- data/sorbet/rbi/gems/rspec-core.rbi +1898 -0
- data/sorbet/rbi/gems/rspec-expectations.rbi +1127 -0
- data/sorbet/rbi/gems/rspec-mocks.rbi +1099 -0
- data/sorbet/rbi/gems/rspec-support.rbi +280 -0
- data/sorbet/rbi/gems/rspec.rbi +15 -0
- data/sorbet/rbi/gems/rubocop-ast.rbi +1355 -0
- data/sorbet/rbi/gems/rubocop.rbi +7253 -0
- data/sorbet/rbi/gems/ruby-progressbar.rbi +304 -0
- data/sorbet/rbi/gems/simplecov-html.rbi +35 -0
- data/sorbet/rbi/gems/simplecov.rbi +406 -0
- data/sorbet/rbi/gems/unicode-display_width.rbi +17 -0
- data/sorbet/rbi/gems/vcr.rbi +572 -0
- data/sorbet/rbi/gems/webmock.rbi +556 -0
- data/sorbet/rbi/gems/yard.rbi +1165 -0
- data/sorbet/rbi/sorbet-typed/lib/rake/all/rake.rbi +645 -0
- data/sorbet/rbi/sorbet-typed/lib/rspec-core/all/rspec-core.rbi +1891 -0
- data/sorbet/rbi/sorbet-typed/lib/rubocop/~>0.85/rubocop.rbi +2072 -0
- data/sorbet/rbi/sorbet-typed/lib/yard/all/yard.rbi +1214 -0
- data/sorbet/rbi/todo.rbi +1 -3
- data/spec/lib/workos/audit_trail_spec.rb +0 -8
- data/spec/lib/workos/directory_sync_spec.rb +347 -40
- data/spec/lib/workos/organizations_spec.rb +164 -0
- data/spec/lib/workos/passwordless_spec.rb +1 -8
- data/spec/lib/workos/portal_spec.rb +17 -123
- data/spec/lib/workos/sso_spec.rb +230 -71
- data/spec/spec_helper.rb +2 -1
- data/spec/support/fixtures/vcr_cassettes/audit_trail/get_events.yml +2 -2
- data/spec/support/fixtures/vcr_cassettes/directory_sync/delete_directory.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_directories/with_after.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_directories/with_before.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/{list_directories_with_domain_param.yml → list_directories/with_domain.yml} +19 -9
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_directories/with_limit.yml +74 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/{list_directories.yml → list_directories/with_no_options.yml} +1 -1
- data/spec/support/fixtures/vcr_cassettes/directory_sync/{list_users_with_directory_param.yml → list_directories/with_search.yml} +22 -11
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_groups/with_after.yml +76 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_groups/with_before.yml +74 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_groups/with_directory.yml +78 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_groups/with_limit.yml +74 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/{list_groups.yml → list_groups/with_no_options.yml} +16 -6
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_groups/with_user.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_users/with_after.yml +86 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_users/with_before.yml +75 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_users/with_directory.yml +93 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_users/with_group.yml +76 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_users/with_limit.yml +75 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/{list_users.yml → list_users/with_no_options.yml} +16 -6
- data/spec/support/fixtures/vcr_cassettes/organization/get.yml +73 -0
- data/spec/support/fixtures/vcr_cassettes/{directory_sync/list_groups_with_directory_param.yml → organization/get_invalid.yml} +21 -11
- data/spec/support/fixtures/vcr_cassettes/organization/update.yml +73 -0
- data/spec/support/fixtures/vcr_cassettes/organization/update_invalid.yml +73 -0
- data/spec/support/fixtures/vcr_cassettes/portal/generate_link_dsync.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/portal/{generate_link.yml → generate_link_sso.yml} +1 -1
- data/spec/support/fixtures/vcr_cassettes/sso/delete_connection_with_invalid_id.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/sso/delete_connection_with_valid_id.yml +70 -0
- data/spec/support/fixtures/vcr_cassettes/sso/{create_connection_with_invalid_source.yml → get_connection_with_invalid_id.yml} +26 -12
- data/spec/support/fixtures/vcr_cassettes/sso/get_connection_with_valid_id.yml +74 -0
- data/spec/support/fixtures/vcr_cassettes/sso/list_connections/with_after.yml +73 -0
- data/spec/support/fixtures/vcr_cassettes/sso/list_connections/with_before.yml +73 -0
- data/spec/support/fixtures/vcr_cassettes/sso/list_connections/with_connection_type.yml +73 -0
- data/spec/support/fixtures/vcr_cassettes/sso/list_connections/with_domain.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/sso/list_connections/with_limit.yml +74 -0
- data/spec/support/fixtures/vcr_cassettes/sso/list_connections/with_no_options.yml +73 -0
- data/spec/support/fixtures/vcr_cassettes/sso/list_connections/with_organization_id.yml +72 -0
- data/workos.gemspec +2 -0
- metadata +122 -33
- data/CODEOWNERS +0 -1
- data/sorbet/rbi/hidden-definitions/errors.txt +0 -24896
- data/sorbet/rbi/hidden-definitions/hidden.rbi +0 -38411
- data/sorbet/rbi/sorbet-typed/lib/bundler/all/bundler.rbi +0 -8684
- data/sorbet/rbi/sorbet-typed/lib/ruby/all/gem.rbi +0 -4222
- data/sorbet/rbi/sorbet-typed/lib/ruby/all/open3.rbi +0 -111
- data/sorbet/rbi/sorbet-typed/lib/ruby/all/resolv.rbi +0 -543
- data/spec/support/fixtures/vcr_cassettes/sso/create_connection_with_valid_source.yml +0 -63
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# typed: true
|
|
3
|
+
|
|
4
|
+
require 'net/http'
|
|
5
|
+
|
|
6
|
+
module WorkOS
|
|
7
|
+
# The Organizations module provides resource methods for working with Organizations
|
|
8
|
+
module Organizations
|
|
9
|
+
class << self
|
|
10
|
+
extend T::Sig
|
|
11
|
+
include Base
|
|
12
|
+
include Client
|
|
13
|
+
|
|
14
|
+
# Retrieve a list of organizations that have connections configured
|
|
15
|
+
# within your WorkOS dashboard.
|
|
16
|
+
#
|
|
17
|
+
# @param [Array<String>] domains Filter organizations to only return those
|
|
18
|
+
# that are associated with the provided domains.
|
|
19
|
+
# @param [String] before A pagination argument used to request
|
|
20
|
+
# organizations before the provided Organization ID.
|
|
21
|
+
# @param [String] after A pagination argument used to request
|
|
22
|
+
# organizations after the provided Organization ID.
|
|
23
|
+
# @param [Integer] limit A pagination argument used to limit the number
|
|
24
|
+
# of listed Organizations that are returned.
|
|
25
|
+
sig do
|
|
26
|
+
params(
|
|
27
|
+
options: T::Hash[Symbol, String],
|
|
28
|
+
).returns(WorkOS::Types::ListStruct)
|
|
29
|
+
end
|
|
30
|
+
def list_organizations(options = {})
|
|
31
|
+
response = execute_request(
|
|
32
|
+
request: get_request(
|
|
33
|
+
path: '/organizations',
|
|
34
|
+
auth: true,
|
|
35
|
+
params: options,
|
|
36
|
+
),
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
parsed_response = JSON.parse(response.body)
|
|
40
|
+
|
|
41
|
+
organizations = parsed_response['data'].map do |organization|
|
|
42
|
+
::WorkOS::Organization.new(organization.to_json)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
WorkOS::Types::ListStruct.new(
|
|
46
|
+
data: organizations,
|
|
47
|
+
list_metadata: parsed_response['listMetadata'],
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Get an Organization
|
|
52
|
+
#
|
|
53
|
+
# @param [String] id Organization unique identifier
|
|
54
|
+
#
|
|
55
|
+
# @example
|
|
56
|
+
# WorkOS::Portal.get_organization(id: 'org_02DRA1XNSJDZ19A31F183ECQW9')
|
|
57
|
+
# => #<WorkOS::Organization:0x00007fb6e4193d20
|
|
58
|
+
# @id="org_02DRA1XNSJDZ19A31F183ECQW9",
|
|
59
|
+
# @name="Foo Corp",
|
|
60
|
+
# @domains=
|
|
61
|
+
# [{:object=>"organization_domain",
|
|
62
|
+
# :id=>"org_domain_01E6PK9N3XMD8RHWF7S66380AR",
|
|
63
|
+
# :domain=>"foo-corp.com"}]>
|
|
64
|
+
#
|
|
65
|
+
# @return [WorkOS::Connection]
|
|
66
|
+
sig { params(id: String).returns(WorkOS::Organization) }
|
|
67
|
+
def get_organization(id:)
|
|
68
|
+
request = get_request(
|
|
69
|
+
auth: true,
|
|
70
|
+
path: "/organizations/#{id}",
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
response = execute_request(request: request)
|
|
74
|
+
|
|
75
|
+
WorkOS::Organization.new(response.body)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Create an organization
|
|
79
|
+
#
|
|
80
|
+
# @param [Array<String>] domains List of domains that belong to the
|
|
81
|
+
# organization
|
|
82
|
+
# @param [String] name A unique, descriptive name for the organization
|
|
83
|
+
sig do
|
|
84
|
+
params(
|
|
85
|
+
domains: T::Array[String],
|
|
86
|
+
name: String,
|
|
87
|
+
).returns(WorkOS::Organization)
|
|
88
|
+
end
|
|
89
|
+
def create_organization(domains:, name:)
|
|
90
|
+
request = post_request(
|
|
91
|
+
auth: true,
|
|
92
|
+
body: { domains: domains, name: name },
|
|
93
|
+
path: '/organizations',
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
response = execute_request(request: request)
|
|
97
|
+
check_and_raise_organization_error(response: response)
|
|
98
|
+
|
|
99
|
+
WorkOS::Organization.new(response.body)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Update an organization
|
|
103
|
+
#
|
|
104
|
+
# @param [String] organization Organization unique identifier
|
|
105
|
+
# @param [Array<String>] domains List of domains that belong to the
|
|
106
|
+
# organization
|
|
107
|
+
# @param [String] name A unique, descriptive name for the organization
|
|
108
|
+
sig do
|
|
109
|
+
params(
|
|
110
|
+
organization: String,
|
|
111
|
+
domains: T::Array[String],
|
|
112
|
+
name: String,
|
|
113
|
+
).returns(WorkOS::Organization)
|
|
114
|
+
end
|
|
115
|
+
def update_organization(organization:, domains:, name:)
|
|
116
|
+
request = put_request(
|
|
117
|
+
auth: true,
|
|
118
|
+
body: { domains: domains, name: name },
|
|
119
|
+
path: "/organizations/#{organization}",
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
response = execute_request(request: request)
|
|
123
|
+
check_and_raise_organization_error(response: response)
|
|
124
|
+
|
|
125
|
+
WorkOS::Organization.new(response.body)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
private
|
|
129
|
+
|
|
130
|
+
sig { params(response: Net::HTTPResponse).void }
|
|
131
|
+
def check_and_raise_organization_error(response:)
|
|
132
|
+
begin
|
|
133
|
+
body = JSON.parse(response.body)
|
|
134
|
+
return unless body['message']
|
|
135
|
+
|
|
136
|
+
message = body['message']
|
|
137
|
+
request_id = response['x-request-id']
|
|
138
|
+
rescue StandardError
|
|
139
|
+
message = 'Something went wrong'
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
raise APIError.new(
|
|
143
|
+
message: message,
|
|
144
|
+
http_status: nil,
|
|
145
|
+
request_id: request_id,
|
|
146
|
+
)
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
data/lib/workos/passwordless.rb
CHANGED
|
@@ -23,8 +23,15 @@ module WorkOS
|
|
|
23
23
|
# received from WorkOS will contain. The state parameter can be used to
|
|
24
24
|
# encode arbitrary information to help restore application state between
|
|
25
25
|
# redirects.
|
|
26
|
+
# @option options [String] connection Optional parameter for the ID of a
|
|
27
|
+
# specific connection. This can be used to create a Passwordless Session
|
|
28
|
+
# for a specific connection rather than using the domain from the email
|
|
29
|
+
# to determine the Organization and Connection.
|
|
26
30
|
# @option options [String] type The type of Passwordless Session to
|
|
27
31
|
# create. Currently, the only supported value is 'MagicLink'.
|
|
32
|
+
# @option options [String] redirect_uri The URI where users are directed
|
|
33
|
+
# after completing the authentication step. Must match a
|
|
34
|
+
# configured redirect URI on your WorkOS dashboard.
|
|
28
35
|
#
|
|
29
36
|
# @return Hash
|
|
30
37
|
sig do
|
|
@@ -33,7 +40,6 @@ module WorkOS
|
|
|
33
40
|
).returns(WorkOS::Types::PasswordlessSessionStruct)
|
|
34
41
|
end
|
|
35
42
|
|
|
36
|
-
# rubocop:disable Metrics/MethodLength
|
|
37
43
|
def create_session(options)
|
|
38
44
|
response = execute_request(
|
|
39
45
|
request: post_request(
|
|
@@ -52,7 +58,6 @@ module WorkOS
|
|
|
52
58
|
link: hash['link'],
|
|
53
59
|
)
|
|
54
60
|
end
|
|
55
|
-
# rubocop:enable Metrics/MethodLength
|
|
56
61
|
|
|
57
62
|
# Send a Passwordless Session via email.
|
|
58
63
|
#
|
data/lib/workos/portal.rb
CHANGED
|
@@ -15,34 +15,10 @@ module WorkOS
|
|
|
15
15
|
GENERATE_LINK_INTENTS = WorkOS::Types::Intent.values.map(&:serialize).
|
|
16
16
|
freeze
|
|
17
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
18
|
# Generate a link to grant access to an organization's Admin Portal
|
|
43
19
|
#
|
|
44
20
|
# @param [String] intent The access scope for the generated Admin Portal
|
|
45
|
-
# link. Valid values are: ["sso"]
|
|
21
|
+
# link. Valid values are: ["sso", "dsync"]
|
|
46
22
|
# @param [String] organization The ID of the organization the Admin
|
|
47
23
|
# Portal link will be generated for.
|
|
48
24
|
# @param [String] The URL that the end user will be redirected to upon
|
|
@@ -55,7 +31,6 @@ module WorkOS
|
|
|
55
31
|
return_url: T.nilable(String),
|
|
56
32
|
).returns(String)
|
|
57
33
|
end
|
|
58
|
-
# rubocop:disable Metrics/MethodLength
|
|
59
34
|
def generate_link(intent:, organization:, return_url: nil)
|
|
60
35
|
validate_intent(intent)
|
|
61
36
|
|
|
@@ -73,70 +48,9 @@ module WorkOS
|
|
|
73
48
|
|
|
74
49
|
JSON.parse(response.body)['link']
|
|
75
50
|
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
51
|
|
|
117
52
|
private
|
|
118
53
|
|
|
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
54
|
sig { params(intent: String).void }
|
|
141
55
|
def validate_intent(intent)
|
|
142
56
|
return if GENERATE_LINK_INTENTS.include?(intent)
|
data/lib/workos/profile.rb
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# typed: true
|
|
3
3
|
|
|
4
|
-
require 'json'
|
|
5
|
-
|
|
6
4
|
module WorkOS
|
|
7
5
|
# The Profile class provides a lighweight wrapper around
|
|
8
6
|
# a normalized response from the various IDPs WorkOS
|
|
@@ -15,7 +13,7 @@ module WorkOS
|
|
|
15
13
|
sig { returns(String) }
|
|
16
14
|
attr_accessor :id, :email, :first_name, :last_name, :connection_id,
|
|
17
15
|
:connection_type, :idp_id, :raw_attributes
|
|
18
|
-
|
|
16
|
+
|
|
19
17
|
sig { params(profile_json: String).void }
|
|
20
18
|
def initialize(profile_json)
|
|
21
19
|
raw = parse_json(profile_json)
|
|
@@ -29,7 +27,6 @@ module WorkOS
|
|
|
29
27
|
@idp_id = raw.idp_id
|
|
30
28
|
@raw_attributes = raw.raw_attributes
|
|
31
29
|
end
|
|
32
|
-
# rubocop:enable Metrics/AbcSize
|
|
33
30
|
|
|
34
31
|
sig { returns(String) }
|
|
35
32
|
def full_name
|
|
@@ -51,7 +48,7 @@ module WorkOS
|
|
|
51
48
|
|
|
52
49
|
private
|
|
53
50
|
|
|
54
|
-
# rubocop:disable Metrics/AbcSize
|
|
51
|
+
# rubocop:disable Metrics/AbcSize
|
|
55
52
|
sig { params(json_string: String).returns(WorkOS::Types::ProfileStruct) }
|
|
56
53
|
def parse_json(json_string)
|
|
57
54
|
hash = JSON.parse(json_string, symbolize_names: true)
|
|
@@ -67,6 +64,6 @@ module WorkOS
|
|
|
67
64
|
raw_attributes: hash[:profile][:raw_attributes],
|
|
68
65
|
)
|
|
69
66
|
end
|
|
70
|
-
# rubocop:enable Metrics/AbcSize
|
|
67
|
+
# rubocop:enable Metrics/AbcSize
|
|
71
68
|
end
|
|
72
69
|
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# typed: true
|
|
3
|
+
|
|
4
|
+
module WorkOS
|
|
5
|
+
# The ProfileAndToken class represents a Profile and a corresponding
|
|
6
|
+
# Access Token. This class is not meant to be instantiated in user space, and
|
|
7
|
+
# is instantiated internally but exposed.
|
|
8
|
+
class ProfileAndToken
|
|
9
|
+
extend T::Sig
|
|
10
|
+
|
|
11
|
+
attr_accessor :access_token, :profile
|
|
12
|
+
|
|
13
|
+
sig { params(profile_and_token_json: String).void }
|
|
14
|
+
def initialize(profile_and_token_json)
|
|
15
|
+
json = JSON.parse(profile_and_token_json, symbolize_names: true)
|
|
16
|
+
|
|
17
|
+
@access_token = T.let(json[:access_token], String)
|
|
18
|
+
@profile = WorkOS::Profile.new(profile_and_token_json)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def to_json(*)
|
|
22
|
+
{
|
|
23
|
+
access_token: access_token,
|
|
24
|
+
profile: profile.to_json,
|
|
25
|
+
}
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
data/lib/workos/sso.rb
CHANGED
|
@@ -6,7 +6,7 @@ require 'uri'
|
|
|
6
6
|
|
|
7
7
|
module WorkOS
|
|
8
8
|
# The SSO module provides convenience methods for working with the WorkOS
|
|
9
|
-
# SSO platform. You'll need a valid API key, a
|
|
9
|
+
# SSO platform. You'll need a valid API key, a client ID, and to have
|
|
10
10
|
# created an SSO connection on your WorkOS dashboard.
|
|
11
11
|
#
|
|
12
12
|
# @see https://docs.workos.com/sso/overview
|
|
@@ -26,7 +26,9 @@ module WorkOS
|
|
|
26
26
|
# required
|
|
27
27
|
# @param [String] provider A provider name for an Identity Provider
|
|
28
28
|
# configured on your WorkOS dashboard. Only 'Google' is supported.
|
|
29
|
-
# @param [String]
|
|
29
|
+
# @param [String] connection The ID for a Connection configured on
|
|
30
|
+
# WorkOS.
|
|
31
|
+
# @param [String] client_id The WorkOS client ID for the environment
|
|
30
32
|
# where you've configured your SSO connection.
|
|
31
33
|
# @param [String] redirect_uri The URI where users are directed
|
|
32
34
|
# after completing the authentication step. Must match a
|
|
@@ -36,7 +38,7 @@ module WorkOS
|
|
|
36
38
|
# @example
|
|
37
39
|
# WorkOS::SSO.authorization_url(
|
|
38
40
|
# domain: 'acme.com',
|
|
39
|
-
#
|
|
41
|
+
# client_id: 'project_01DG5TGK363GRVXP3ZS40WNGEZ',
|
|
40
42
|
# redirect_uri: 'https://workos.com/callback',
|
|
41
43
|
# state: {
|
|
42
44
|
# next_page: '/docs'
|
|
@@ -51,25 +53,36 @@ module WorkOS
|
|
|
51
53
|
# @return [String]
|
|
52
54
|
sig do
|
|
53
55
|
params(
|
|
54
|
-
project_id: String,
|
|
55
56
|
redirect_uri: String,
|
|
57
|
+
client_id: T.nilable(String),
|
|
56
58
|
domain: T.nilable(String),
|
|
57
59
|
provider: T.nilable(String),
|
|
60
|
+
connection: T.nilable(String),
|
|
58
61
|
state: T.nilable(String),
|
|
59
62
|
).returns(String)
|
|
60
63
|
end
|
|
61
64
|
def authorization_url(
|
|
62
|
-
|
|
65
|
+
redirect_uri:,
|
|
66
|
+
client_id: nil,
|
|
67
|
+
domain: nil,
|
|
68
|
+
provider: nil,
|
|
69
|
+
connection: nil,
|
|
70
|
+
state: ''
|
|
63
71
|
)
|
|
64
|
-
|
|
72
|
+
validate_authorization_url_arguments(
|
|
73
|
+
provider: provider,
|
|
74
|
+
domain: domain,
|
|
75
|
+
connection: connection,
|
|
76
|
+
)
|
|
65
77
|
|
|
66
78
|
query = URI.encode_www_form({
|
|
67
|
-
client_id:
|
|
79
|
+
client_id: client_id,
|
|
68
80
|
redirect_uri: redirect_uri,
|
|
69
81
|
response_type: 'code',
|
|
70
82
|
state: state,
|
|
71
83
|
domain: domain,
|
|
72
84
|
provider: provider,
|
|
85
|
+
connection: connection,
|
|
73
86
|
}.compact)
|
|
74
87
|
|
|
75
88
|
"https://#{WorkOS::API_HOSTNAME}/sso/authorize?#{query}"
|
|
@@ -78,73 +91,77 @@ module WorkOS
|
|
|
78
91
|
# Fetch the profile details for the authenticated SSO user.
|
|
79
92
|
#
|
|
80
93
|
# @param [String] code The authorization code provided in the callback URL
|
|
81
|
-
# @param [String]
|
|
82
|
-
# where you've
|
|
94
|
+
# @param [String] client_id The WorkOS client ID for the environment
|
|
95
|
+
# where you've configured your SSO connection
|
|
83
96
|
#
|
|
84
|
-
# @
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
# @first_name="WorkOS",
|
|
93
|
-
# @connection_type="OktaSAML",
|
|
94
|
-
# @last_name="Demo",
|
|
95
|
-
# @idp_id="00u1klkowm8EGah2H357",
|
|
96
|
-
# @access_token="01DVX6QBS3EG6FHY2ESAA5Q65X"
|
|
97
|
-
# >
|
|
98
|
-
#
|
|
99
|
-
# @return [WorkOS::Profile]
|
|
100
|
-
sig { params(code: String, project_id: String).returns(WorkOS::Profile) }
|
|
101
|
-
def profile(code:, project_id:)
|
|
97
|
+
# @return [WorkOS::ProfileAndToken]
|
|
98
|
+
sig do
|
|
99
|
+
params(
|
|
100
|
+
code: String,
|
|
101
|
+
client_id: T.nilable(String),
|
|
102
|
+
).returns(WorkOS::ProfileAndToken)
|
|
103
|
+
end
|
|
104
|
+
def profile_and_token(code:, client_id: nil)
|
|
102
105
|
body = {
|
|
103
|
-
client_id:
|
|
106
|
+
client_id: client_id,
|
|
104
107
|
client_secret: WorkOS.key!,
|
|
105
108
|
grant_type: 'authorization_code',
|
|
106
109
|
code: code,
|
|
107
110
|
}
|
|
108
111
|
|
|
109
112
|
response = client.request(post_request(path: '/sso/token', body: body))
|
|
110
|
-
|
|
113
|
+
check_and_raise_profile_and_token_error(response: response)
|
|
111
114
|
|
|
112
|
-
WorkOS::
|
|
115
|
+
WorkOS::ProfileAndToken.new(response.body)
|
|
113
116
|
end
|
|
114
117
|
|
|
115
|
-
#
|
|
116
|
-
# Enterprise users can begin signing into your application.
|
|
117
|
-
#
|
|
118
|
-
# @param [String] token The Draft Connection token that's been provided to
|
|
119
|
-
# you by the WorkOS.js
|
|
118
|
+
# Retrieve connections.
|
|
120
119
|
#
|
|
121
|
-
# @
|
|
122
|
-
#
|
|
123
|
-
#
|
|
124
|
-
#
|
|
125
|
-
#
|
|
120
|
+
# @param [Hash] options An options hash
|
|
121
|
+
# @option options [String] connection_type Authentication service
|
|
122
|
+
# provider descriptor.
|
|
123
|
+
# @option options [String] domain The domain of the connection to be
|
|
124
|
+
# retrieved.
|
|
125
|
+
# @option options [String] organization_id The id of the organization
|
|
126
|
+
# of the connections to be retrieved.
|
|
127
|
+
# @option options [String] limit Maximum number of records to return.
|
|
128
|
+
# @option options [String] before Pagination cursor to receive records
|
|
129
|
+
# before a provided Connection ID.
|
|
130
|
+
# @option options [String] after Pagination cursor to receive records
|
|
131
|
+
# before a provided Connection ID.
|
|
126
132
|
#
|
|
127
|
-
# @return [
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
133
|
+
# @return [Hash]
|
|
134
|
+
sig do
|
|
135
|
+
params(
|
|
136
|
+
options: T::Hash[Symbol, String],
|
|
137
|
+
).returns(WorkOS::Types::ListStruct)
|
|
138
|
+
end
|
|
139
|
+
def list_connections(options = {})
|
|
140
|
+
response = execute_request(
|
|
141
|
+
request: get_request(
|
|
142
|
+
path: '/connections',
|
|
143
|
+
auth: true,
|
|
144
|
+
params: options,
|
|
145
|
+
),
|
|
134
146
|
)
|
|
135
147
|
|
|
136
|
-
|
|
148
|
+
parsed_response = JSON.parse(response.body)
|
|
149
|
+
connections = parsed_response['data'].map do |connection|
|
|
150
|
+
::WorkOS::Connection.new(connection.to_json)
|
|
151
|
+
end
|
|
137
152
|
|
|
138
|
-
|
|
153
|
+
WorkOS::Types::ListStruct.new(
|
|
154
|
+
data: connections,
|
|
155
|
+
list_metadata: parsed_response['listMetadata'],
|
|
156
|
+
)
|
|
139
157
|
end
|
|
140
158
|
|
|
141
|
-
#
|
|
159
|
+
# Get a Connection
|
|
142
160
|
#
|
|
143
|
-
# @param [String]
|
|
144
|
-
# to you by WorkOS.js
|
|
161
|
+
# @param [String] id Connection unique identifier
|
|
145
162
|
#
|
|
146
163
|
# @example
|
|
147
|
-
# WorkOS::SSO.
|
|
164
|
+
# WorkOS::SSO.get_connection(id: 'conn_02DRA1XNSJDZ19A31F183ECQW9')
|
|
148
165
|
# => #<WorkOS::Connection:0x00007fb6e4193d20
|
|
149
166
|
# @id="conn_02DRA1XNSJDZ19A31F183ECQW9",
|
|
150
167
|
# @name="Foo Corp",
|
|
@@ -155,12 +172,11 @@ module WorkOS
|
|
|
155
172
|
# :domain=>"example.com"}]>
|
|
156
173
|
#
|
|
157
174
|
# @return [WorkOS::Connection]
|
|
158
|
-
sig { params(
|
|
159
|
-
def
|
|
160
|
-
request =
|
|
175
|
+
sig { params(id: String).returns(WorkOS::Connection) }
|
|
176
|
+
def get_connection(id:)
|
|
177
|
+
request = get_request(
|
|
161
178
|
auth: true,
|
|
162
|
-
path:
|
|
163
|
-
body: { source: source },
|
|
179
|
+
path: "/connections/#{id}",
|
|
164
180
|
)
|
|
165
181
|
|
|
166
182
|
response = execute_request(request: request)
|
|
@@ -168,17 +184,44 @@ module WorkOS
|
|
|
168
184
|
WorkOS::Connection.new(response.body)
|
|
169
185
|
end
|
|
170
186
|
|
|
187
|
+
# Delete a Connection
|
|
188
|
+
#
|
|
189
|
+
# @param [String] id Connection unique identifier
|
|
190
|
+
#
|
|
191
|
+
# @example
|
|
192
|
+
# WorkOS::SSO.delete_connection(id: 'conn_02DRA1XNSJDZ19A31F183ECQW9')
|
|
193
|
+
# => true
|
|
194
|
+
#
|
|
195
|
+
# @return [Bool] - returns `true` if successful
|
|
196
|
+
sig { params(id: String).returns(T::Boolean) }
|
|
197
|
+
def delete_connection(id:)
|
|
198
|
+
request = delete_request(
|
|
199
|
+
auth: true,
|
|
200
|
+
path: "/connections/#{id}",
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
response = execute_request(request: request)
|
|
204
|
+
|
|
205
|
+
response.is_a? Net::HTTPSuccess
|
|
206
|
+
end
|
|
207
|
+
|
|
171
208
|
private
|
|
172
209
|
|
|
173
210
|
sig do
|
|
174
211
|
params(
|
|
175
212
|
domain: T.nilable(String),
|
|
176
213
|
provider: T.nilable(String),
|
|
214
|
+
connection: T.nilable(String),
|
|
177
215
|
).void
|
|
178
216
|
end
|
|
179
|
-
def
|
|
180
|
-
|
|
181
|
-
|
|
217
|
+
def validate_authorization_url_arguments(
|
|
218
|
+
domain:,
|
|
219
|
+
provider:,
|
|
220
|
+
connection:
|
|
221
|
+
)
|
|
222
|
+
if [domain, provider, connection].all?(&:nil?)
|
|
223
|
+
raise ArgumentError, 'Either connection, domain, or ' \
|
|
224
|
+
'provider is required.'
|
|
182
225
|
end
|
|
183
226
|
|
|
184
227
|
return unless provider && !PROVIDERS.include?(provider)
|
|
@@ -187,12 +230,11 @@ module WorkOS
|
|
|
187
230
|
" `provider` must be in #{PROVIDERS}"
|
|
188
231
|
end
|
|
189
232
|
|
|
190
|
-
# rubocop:disable Metrics/MethodLength
|
|
191
233
|
sig { params(response: Net::HTTPResponse).void }
|
|
192
|
-
def
|
|
234
|
+
def check_and_raise_profile_and_token_error(response:)
|
|
193
235
|
begin
|
|
194
236
|
body = JSON.parse(response.body)
|
|
195
|
-
return if body['profile']
|
|
237
|
+
return if body['access_token'] && body['profile']
|
|
196
238
|
|
|
197
239
|
message = body['message']
|
|
198
240
|
request_id = response['x-request-id']
|
|
@@ -206,7 +248,6 @@ module WorkOS
|
|
|
206
248
|
request_id: request_id,
|
|
207
249
|
)
|
|
208
250
|
end
|
|
209
|
-
# rubocop:enable Metrics/MethodLength
|
|
210
251
|
end
|
|
211
252
|
end
|
|
212
253
|
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# typed: strict
|
|
3
|
+
|
|
4
|
+
module WorkOS
|
|
5
|
+
module Types
|
|
6
|
+
# This DirectoryGroupStruct acts as a typed interface
|
|
7
|
+
# for the DirectoryGroup class
|
|
8
|
+
class DirectoryGroupStruct < T::Struct
|
|
9
|
+
const :id, String
|
|
10
|
+
const :name, String
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# typed: strict
|
|
3
|
+
|
|
4
|
+
module WorkOS
|
|
5
|
+
module Types
|
|
6
|
+
# This DirectoryStruct acts as a typed interface
|
|
7
|
+
# for the Directory class
|
|
8
|
+
class DirectoryStruct < T::Struct
|
|
9
|
+
const :id, String
|
|
10
|
+
const :name, String
|
|
11
|
+
const :domain, String
|
|
12
|
+
const :type, String
|
|
13
|
+
const :state, String
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|