workos 1.5.1 → 2.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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/.ruby-version +1 -1
  4. data/.semaphore/semaphore.yml +2 -2
  5. data/Gemfile.lock +3 -3
  6. data/lib/workos/connection.rb +1 -1
  7. data/lib/workos/directory.rb +2 -2
  8. data/lib/workos/directory_sync.rb +29 -0
  9. data/lib/workos/errors.rb +4 -0
  10. data/lib/workos/organization.rb +4 -1
  11. data/lib/workos/organizations.rb +18 -4
  12. data/lib/workos/profile.rb +7 -2
  13. data/lib/workos/sso.rb +38 -15
  14. data/lib/workos/types/connection_struct.rb +1 -1
  15. data/lib/workos/types/directory_struct.rb +2 -2
  16. data/lib/workos/types/organization_struct.rb +1 -0
  17. data/lib/workos/types/profile_struct.rb +1 -0
  18. data/lib/workos/types/webhook_struct.rb +14 -0
  19. data/lib/workos/types.rb +1 -0
  20. data/lib/workos/version.rb +1 -1
  21. data/lib/workos/webhook.rb +47 -0
  22. data/lib/workos/webhooks.rb +168 -0
  23. data/lib/workos.rb +3 -0
  24. data/spec/lib/workos/directory_sync_spec.rb +43 -10
  25. data/spec/lib/workos/sso_spec.rb +142 -2
  26. data/spec/lib/workos/webhooks_spec.rb +190 -0
  27. data/spec/support/fixtures/vcr_cassettes/{organization/update_invalid.yml → directory_sync/get_directory_with_invalid_id.yml} +35 -25
  28. data/spec/support/fixtures/vcr_cassettes/directory_sync/get_directory_with_valid_id.yml +84 -0
  29. data/spec/support/fixtures/vcr_cassettes/directory_sync/list_directories/with_after.yml +34 -22
  30. data/spec/support/fixtures/vcr_cassettes/directory_sync/list_directories/with_before.yml +36 -22
  31. data/spec/support/fixtures/vcr_cassettes/directory_sync/list_directories/with_domain.yml +30 -19
  32. data/spec/support/fixtures/vcr_cassettes/directory_sync/list_directories/with_limit.yml +31 -20
  33. data/spec/support/fixtures/vcr_cassettes/directory_sync/list_directories/with_no_options.yml +39 -21
  34. data/spec/support/fixtures/vcr_cassettes/directory_sync/list_directories/with_search.yml +32 -20
  35. data/spec/support/fixtures/vcr_cassettes/organization/create.yml +1 -1
  36. data/spec/support/fixtures/vcr_cassettes/organization/get.yml +1 -1
  37. data/spec/support/fixtures/vcr_cassettes/organization/list.yml +4 -4
  38. data/spec/support/fixtures/vcr_cassettes/organization/update.yml +1 -1
  39. data/spec/support/fixtures/vcr_cassettes/sso/profile.yml +1 -1
  40. data/spec/support/profile.txt +1 -1
  41. data/spec/support/webhook_payload.txt +1 -0
  42. metadata +14 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5aecf9fa40a85a4623e12f1ba6804c87fde006931bed9a443327f6851d714df1
4
- data.tar.gz: 63d0daa095bf705643906247371d36a15f7ec9aac2b9d5b04293fb7e135a1d57
3
+ metadata.gz: 0500b789496692e53bc6d47d80a21a0b8b802325e1e0f876fe0b94a775aa4f05
4
+ data.tar.gz: a7e8e350e7fb1336496e9a09ee7543a120e1b6ed62cc60dffbe1b4ccd2dd4712
5
5
  SHA512:
6
- metadata.gz: f6c47e8d64139d4e3452dca04e6466b177b8c029b97779e58b93407d5e08f9c886076a0101a9b272376fe33e9ee16c180464e25b3be42a6089b921108187eeb1
7
- data.tar.gz: 577c4b464439eed2216d1011d637d619cd0af9c69f985f5d56f56ce58f6c72deb71f0d2dc66efc1014daa7d973cd2c1d3e95a2f729627658d4235ce2da683184
6
+ metadata.gz: 549c9210c2d765b2d6f264e62f285f7b96c9225e53724798216eb08167be5451825a729f36273fbda7fb713b9a1db49025b3cc6a308a98b969ca0a3631c8ffd9
7
+ data.tar.gz: 4a3fdf8d50681db812a0de37c56d93b03c6bb194a892280cfc9d287612ba7494a1bd777f92b28538b12d978b0a1dc354ca7c82e12fd1707c440643c7215685cb
data/.rubocop.yml CHANGED
@@ -9,7 +9,7 @@ Layout/LineLength:
9
9
  - 'VCR\.use_cassette'
10
10
  - '(\A|\s)/.*?/'
11
11
  Metrics/BlockLength:
12
- ExcludedMethods: ['describe', 'context']
12
+ ExcludedMethods: ['describe', 'context', 'before']
13
13
  Metrics/MethodLength:
14
14
  Max: 15
15
15
  Metrics/ModuleLength:
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.0.1
1
+ 3.0.2
@@ -66,10 +66,10 @@ blocks:
66
66
  - sem-version ruby 2.7.3
67
67
  - bundle install
68
68
  - bundle exec rspec
69
- - name: Ruby 3.0.1
69
+ - name: Ruby 3.0.2
70
70
  commands:
71
71
  - checkout
72
- - sem-version ruby 3.0.1
72
+ - sem-version ruby 3.0.2
73
73
  - bundle install
74
74
  - bundle exec rspec
75
75
  promotions:
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- workos (1.5.1)
4
+ workos (2.1.0)
5
5
  sorbet-runtime (~> 0.5)
6
6
 
7
7
  GEM
@@ -60,7 +60,7 @@ GEM
60
60
  simplecov_json_formatter (0.1.2)
61
61
  sorbet (0.5.6388)
62
62
  sorbet-static (= 0.5.6388)
63
- sorbet-runtime (0.5.9094)
63
+ sorbet-runtime (0.5.9300)
64
64
  sorbet-static (0.5.6388-universal-darwin-14)
65
65
  sorbet-static (0.5.6388-universal-darwin-15)
66
66
  sorbet-static (0.5.6388-universal-darwin-16)
@@ -93,4 +93,4 @@ DEPENDENCIES
93
93
  yard
94
94
 
95
95
  BUNDLED WITH
96
- 2.2.16
96
+ 2.2.33
@@ -21,7 +21,7 @@ module WorkOS
21
21
  @name = T.let(raw.name, String)
22
22
  @connection_type = T.let(raw.connection_type, String)
23
23
  @domains = T.let(raw.domains, Array)
24
- @organization_id = T.let(raw.organization_id, String)
24
+ @organization_id = raw.organization_id
25
25
  @state = T.let(raw.state, String)
26
26
  @status = T.let(raw.status, String)
27
27
  @created_at = T.let(raw.created_at, String)
@@ -17,10 +17,10 @@ module WorkOS
17
17
 
18
18
  @id = T.let(raw.id, String)
19
19
  @name = T.let(raw.name, String)
20
- @domain = T.let(raw.domain, String)
20
+ @domain = raw.domain
21
21
  @type = T.let(raw.type, String)
22
22
  @state = T.let(raw.state, String)
23
- @organization_id = T.let(raw.organization_id, String)
23
+ @organization_id = raw.organization_id
24
24
  @created_at = T.let(raw.created_at, String)
25
25
  @updated_at = T.let(raw.updated_at, String)
26
26
  end
@@ -53,6 +53,35 @@ module WorkOS
53
53
  )
54
54
  end
55
55
 
56
+ # Retrieve directory.
57
+ #
58
+ # @param [String] id Directory unique identifier
59
+ #
60
+ # @example
61
+ # WorkOS::SSO.get_directory(id: 'directory_01FK17DWRHH7APAFXT5B52PV0W')
62
+ # => #<WorkOS::Directory:0x00007fb6e4193d20
63
+ # @id="directory_01FK17DWRHH7APAFXT5B52PV0W",
64
+ # @name="Foo Corp",
65
+ # @domain="foo-corp.com",
66
+ # @type="okta scim v2.0",
67
+ # @state="linked",
68
+ # @organization_id="org_01F6Q6TFP7RD2PF6J03ANNWDKV",
69
+ # @created_at="2021-10-27T15:55:47.856Z",
70
+ # @updated_at="2021-10-27T16:03:43.990Z"
71
+ #
72
+ # @return [WorkOS::Directory]
73
+ sig { params(id: String).returns(WorkOS::Directory) }
74
+ def get_directory(id:)
75
+ request = get_request(
76
+ auth: true,
77
+ path: "/directories/#{id}",
78
+ )
79
+
80
+ response = execute_request(request: request)
81
+
82
+ WorkOS::Directory.new(response.body)
83
+ end
84
+
56
85
  # Retrieve directory groups.
57
86
  #
58
87
  # @param [Hash] options An options hash
data/lib/workos/errors.rb CHANGED
@@ -55,4 +55,8 @@ module WorkOS
55
55
  # InvalidRequestError is raised when a request is initiated with invalid
56
56
  # parameters.
57
57
  class InvalidRequestError < WorkOSError; end
58
+
59
+ # SignatureVerificationError is raised when the signature verification for a
60
+ # webhook fails
61
+ class SignatureVerificationError < WorkOSError; end
58
62
  end
@@ -8,7 +8,7 @@ module WorkOS
8
8
  class Organization
9
9
  extend T::Sig
10
10
 
11
- attr_accessor :id, :domains, :name, :created_at, :updated_at
11
+ attr_accessor :id, :domains, :name, :allow_profiles_outside_organization, :created_at, :updated_at
12
12
 
13
13
  sig { params(json: String).void }
14
14
  def initialize(json)
@@ -16,6 +16,7 @@ module WorkOS
16
16
 
17
17
  @id = T.let(raw.id, String)
18
18
  @name = T.let(raw.name, String)
19
+ @allow_profiles_outside_organization = T.let(raw.allow_profiles_outside_organization, T::Boolean)
19
20
  @domains = T.let(raw.domains, Array)
20
21
  @created_at = T.let(raw.created_at, String)
21
22
  @updated_at = T.let(raw.updated_at, String)
@@ -25,6 +26,7 @@ module WorkOS
25
26
  {
26
27
  id: id,
27
28
  name: name,
29
+ allow_profiles_outside_organization: allow_profiles_outside_organization,
28
30
  domains: domains,
29
31
  created_at: created_at,
30
32
  updated_at: updated_at,
@@ -44,6 +46,7 @@ module WorkOS
44
46
  WorkOS::Types::OrganizationStruct.new(
45
47
  id: hash[:id],
46
48
  name: hash[:name],
49
+ allow_profiles_outside_organization: hash[:allow_profiles_outside_organization],
47
50
  domains: hash[:domains],
48
51
  created_at: hash[:created_at],
49
52
  updated_at: hash[:updated_at],
@@ -80,16 +80,23 @@ module WorkOS
80
80
  # @param [Array<String>] domains List of domains that belong to the
81
81
  # organization
82
82
  # @param [String] name A unique, descriptive name for the organization
83
+ # @param [Boolean, nil] allow_profiles_outside_organization Whether Connections
84
+ # within the Organization allow profiles that are outside of the Organization's configured User Email Domains.
83
85
  sig do
84
86
  params(
85
87
  domains: T::Array[String],
86
88
  name: String,
89
+ allow_profiles_outside_organization: T.nilable(T::Boolean),
87
90
  ).returns(WorkOS::Organization)
88
91
  end
89
- def create_organization(domains:, name:)
92
+ def create_organization(domains:, name:, allow_profiles_outside_organization: nil)
90
93
  request = post_request(
91
94
  auth: true,
92
- body: { domains: domains, name: name },
95
+ body: {
96
+ domains: domains,
97
+ name: name,
98
+ allow_profiles_outside_organization: allow_profiles_outside_organization,
99
+ },
93
100
  path: '/organizations',
94
101
  )
95
102
 
@@ -105,17 +112,24 @@ module WorkOS
105
112
  # @param [Array<String>] domains List of domains that belong to the
106
113
  # organization
107
114
  # @param [String] name A unique, descriptive name for the organization
115
+ # @param [Boolean, nil] allow_profiles_outside_organization Whether Connections
116
+ # within the Organization allow profiles that are outside of the Organization's configured User Email Domains.
108
117
  sig do
109
118
  params(
110
119
  organization: String,
111
120
  domains: T::Array[String],
112
121
  name: String,
122
+ allow_profiles_outside_organization: T.nilable(T::Boolean),
113
123
  ).returns(WorkOS::Organization)
114
124
  end
115
- def update_organization(organization:, domains:, name:)
125
+ def update_organization(organization:, domains:, name:, allow_profiles_outside_organization: nil)
116
126
  request = put_request(
117
127
  auth: true,
118
- body: { domains: domains, name: name },
128
+ body: {
129
+ domains: domains,
130
+ name: name,
131
+ allow_profiles_outside_organization: allow_profiles_outside_organization,
132
+ },
119
133
  path: "/organizations/#{organization}",
120
134
  )
121
135
 
@@ -11,9 +11,10 @@ module WorkOS
11
11
  extend T::Sig
12
12
 
13
13
  sig { returns(String) }
14
- attr_accessor :id, :email, :first_name, :last_name, :connection_id,
15
- :connection_type, :idp_id, :raw_attributes
14
+ attr_accessor :id, :email, :first_name, :last_name, :organization_id,
15
+ :connection_id, :connection_type, :idp_id, :raw_attributes
16
16
 
17
+ # rubocop:disable Metrics/AbcSize
17
18
  sig { params(profile_json: String).void }
18
19
  def initialize(profile_json)
19
20
  raw = parse_json(profile_json)
@@ -22,11 +23,13 @@ module WorkOS
22
23
  @email = T.let(raw.email, String)
23
24
  @first_name = raw.first_name
24
25
  @last_name = raw.last_name
26
+ @organization_id = raw.organization_id
25
27
  @connection_id = T.let(raw.connection_id, String)
26
28
  @connection_type = T.let(raw.connection_type, String)
27
29
  @idp_id = raw.idp_id
28
30
  @raw_attributes = raw.raw_attributes
29
31
  end
32
+ # rubocop:enable Metrics/AbcSize
30
33
 
31
34
  sig { returns(String) }
32
35
  def full_name
@@ -39,6 +42,7 @@ module WorkOS
39
42
  email: email,
40
43
  first_name: first_name,
41
44
  last_name: last_name,
45
+ organization_id: organization_id,
42
46
  connection_id: connection_id,
43
47
  connection_type: connection_type,
44
48
  idp_id: idp_id,
@@ -57,6 +61,7 @@ module WorkOS
57
61
  email: hash[:email],
58
62
  first_name: hash[:first_name],
59
63
  last_name: hash[:last_name],
64
+ organization_id: hash[:organization_id],
60
65
  connection_id: hash[:connection_id],
61
66
  connection_type: hash[:connection_type],
62
67
  idp_id: hash[:idp_id],
data/lib/workos/sso.rb CHANGED
@@ -21,23 +21,27 @@ module WorkOS
21
21
  # Generate an Oauth2 authorization URL where your users will
22
22
  # authenticate using the configured SSO Identity Provider.
23
23
  #
24
+ # @param [String] redirect_uri The URI where users are directed
25
+ # after completing the authentication step. Must match a
26
+ # configured redirect URI on your WorkOS dashboard.
27
+ # @param [String] client_id The WorkOS client ID for the environment
28
+ # where you've configured your SSO connection.
24
29
  # @param [String] domain The domain for the relevant SSO Connection
25
- # configured on your WorkOS dashboard. One of provider or domain is
26
- # required
30
+ # configured on your WorkOS dashboard. One of provider, domain,
31
+ # connection, or organization is required.
32
+ # The domain is deprecated.
27
33
  # @param [String] provider A provider name for an Identity Provider
28
- # configured on your WorkOS dashboard. Only 'Google' is supported.
34
+ # configured on your WorkOS dashboard. Only 'GoogleOAuth' and
35
+ # 'MicrosoftOAuth' are supported.
29
36
  # @param [String] connection The ID for a Connection configured on
30
37
  # WorkOS.
31
- # @param [String] client_id The WorkOS client ID for the environment
32
- # where you've configured your SSO connection.
33
- # @param [String] redirect_uri The URI where users are directed
34
- # after completing the authentication step. Must match a
35
- # configured redirect URI on your WorkOS dashboard.
36
- # @param [String] state An aribtrary state object
38
+ # @param [String] organization The ID for an Organization configured
39
+ # on WorkOS.
40
+ # @param [String] state An arbitrary state object
37
41
  # that is preserved and available to the client in the response.
38
42
  # @example
39
43
  # WorkOS::SSO.authorization_url(
40
- # domain: 'acme.com',
44
+ # connection: 'conn_123',
41
45
  # client_id: 'project_01DG5TGK363GRVXP3ZS40WNGEZ',
42
46
  # redirect_uri: 'https://workos.com/callback',
43
47
  # state: {
@@ -45,19 +49,23 @@ module WorkOS
45
49
  # }.to_s
46
50
  # )
47
51
  #
48
- # => "https://api.workos.com/sso/authorize?domain=acme.com" \
52
+ # => "https://api.workos.com/sso/authorize?connection=conn_123" \
49
53
  # "&client_id=project_01DG5TGK363GRVXP3ZS40WNGEZ" \
50
54
  # "&redirect_uri=https%3A%2F%2Fworkos.com%2Fcallback&" \
51
55
  # "response_type=code&state=%7B%3Anext_page%3D%3E%22%2Fdocs%22%7D"
52
56
  #
53
57
  # @return [String]
58
+ # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists
54
59
  sig do
55
60
  params(
56
61
  redirect_uri: String,
57
62
  client_id: T.nilable(String),
58
63
  domain: T.nilable(String),
64
+ domain_hint: T.nilable(String),
65
+ login_hint: T.nilable(String),
59
66
  provider: T.nilable(String),
60
67
  connection: T.nilable(String),
68
+ organization: T.nilable(String),
61
69
  state: T.nilable(String),
62
70
  ).returns(String)
63
71
  end
@@ -65,14 +73,23 @@ module WorkOS
65
73
  redirect_uri:,
66
74
  client_id: nil,
67
75
  domain: nil,
76
+ domain_hint: nil,
77
+ login_hint: nil,
68
78
  provider: nil,
69
79
  connection: nil,
80
+ organization: nil,
70
81
  state: ''
71
82
  )
83
+ if domain
84
+ warn '[DEPRECATION] `domain` is deprecated.
85
+ Please use `organization` instead.'
86
+ end
87
+
72
88
  validate_authorization_url_arguments(
73
89
  provider: provider,
74
90
  domain: domain,
75
91
  connection: connection,
92
+ organization: organization,
76
93
  )
77
94
 
78
95
  query = URI.encode_www_form({
@@ -81,12 +98,16 @@ module WorkOS
81
98
  response_type: 'code',
82
99
  state: state,
83
100
  domain: domain,
101
+ domain_hint: domain_hint,
102
+ login_hint: login_hint,
84
103
  provider: provider,
85
104
  connection: connection,
105
+ organization: organization,
86
106
  }.compact)
87
107
 
88
108
  "https://#{WorkOS::API_HOSTNAME}/sso/authorize?#{query}"
89
109
  end
110
+ # rubocop:enable Metrics/MethodLength, Metrics/ParameterLists
90
111
 
91
112
  sig do
92
113
  params(
@@ -229,16 +250,18 @@ module WorkOS
229
250
  domain: T.nilable(String),
230
251
  provider: T.nilable(String),
231
252
  connection: T.nilable(String),
253
+ organization: T.nilable(String),
232
254
  ).void
233
255
  end
234
256
  def validate_authorization_url_arguments(
235
257
  domain:,
236
258
  provider:,
237
- connection:
259
+ connection:,
260
+ organization:
238
261
  )
239
- if [domain, provider, connection].all?(&:nil?)
240
- raise ArgumentError, 'Either connection, domain, or ' \
241
- 'provider is required.'
262
+ if [domain, provider, connection, organization].all?(&:nil?)
263
+ raise ArgumentError, 'Either connection, domain, ' \
264
+ 'provider, or organization is required.'
242
265
  end
243
266
 
244
267
  return unless provider && !PROVIDERS.include?(provider)
@@ -10,7 +10,7 @@ module WorkOS
10
10
  const :name, String
11
11
  const :connection_type, String
12
12
  const :domains, T::Array[T.untyped]
13
- const :organization_id, String
13
+ const :organization_id, T.nilable(String)
14
14
  const :state, String
15
15
  const :status, String
16
16
  const :created_at, String
@@ -8,10 +8,10 @@ module WorkOS
8
8
  class DirectoryStruct < T::Struct
9
9
  const :id, String
10
10
  const :name, String
11
- const :domain, String
11
+ const :domain, T.nilable(String)
12
12
  const :type, String
13
13
  const :state, String
14
- const :organization_id, String
14
+ const :organization_id, T.nilable(String)
15
15
  const :created_at, String
16
16
  const :updated_at, String
17
17
  end
@@ -8,6 +8,7 @@ module WorkOS
8
8
  class OrganizationStruct < T::Struct
9
9
  const :id, String
10
10
  const :name, String
11
+ const :allow_profiles_outside_organization, T::Boolean
11
12
  const :domains, T::Array[T.untyped]
12
13
  const :created_at, String
13
14
  const :updated_at, String
@@ -10,6 +10,7 @@ module WorkOS
10
10
  const :email, String
11
11
  const :first_name, T.nilable(String)
12
12
  const :last_name, T.nilable(String)
13
+ const :organization_id, T.nilable(String)
13
14
  const :connection_id, String
14
15
  const :connection_type, String
15
16
  const :idp_id, T.nilable(String)
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+ # typed: strict
3
+
4
+ module WorkOS
5
+ module Types
6
+ # This WebhookStruct acts as a typed interface
7
+ # for the Webhook class
8
+ class WebhookStruct < T::Struct
9
+ const :id, String
10
+ const :event, String
11
+ const :data, T::Hash[Symbol, Object]
12
+ end
13
+ end
14
+ end
data/lib/workos/types.rb CHANGED
@@ -15,5 +15,6 @@ module WorkOS
15
15
  require_relative 'types/profile_struct'
16
16
  require_relative 'types/provider_enum'
17
17
  require_relative 'types/directory_user_struct'
18
+ require_relative 'types/webhook_struct'
18
19
  end
19
20
  end
@@ -2,5 +2,5 @@
2
2
  # typed: strong
3
3
 
4
4
  module WorkOS
5
- VERSION = '1.5.1'
5
+ VERSION = '2.1.0'
6
6
  end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ module WorkOS
5
+ # The Webhook class provides a lightweight wrapper around
6
+ # a WorkOS Webhook resource. This class is not meant to be instantiated
7
+ # in user space, and is instantiated internally but exposed.
8
+ class Webhook
9
+ extend T::Sig
10
+
11
+ attr_accessor :id, :event, :data
12
+
13
+ sig { params(json: String).void }
14
+ def initialize(json)
15
+ raw = parse_json(json)
16
+
17
+ @id = T.let(raw.id, String)
18
+ @event = T.let(raw.event, String)
19
+ @data = raw.data
20
+ end
21
+
22
+ def to_json(*)
23
+ {
24
+ id: id,
25
+ event: event,
26
+ data: data,
27
+ }
28
+ end
29
+
30
+ private
31
+
32
+ sig do
33
+ params(
34
+ json_string: String,
35
+ ).returns(WorkOS::Types::WebhookStruct)
36
+ end
37
+ def parse_json(json_string)
38
+ hash = JSON.parse(json_string, symbolize_names: true)
39
+
40
+ WorkOS::Types::WebhookStruct.new(
41
+ id: hash[:id],
42
+ event: hash[:event],
43
+ data: hash[:data],
44
+ )
45
+ end
46
+ end
47
+ end