workos 1.5.1 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -1
- data/Gemfile.lock +1 -1
- data/lib/workos/errors.rb +4 -0
- data/lib/workos/organization.rb +4 -1
- data/lib/workos/organizations.rb +18 -4
- data/lib/workos/profile.rb +7 -2
- data/lib/workos/types/organization_struct.rb +1 -0
- data/lib/workos/types/profile_struct.rb +1 -0
- data/lib/workos/types/webhook_struct.rb +14 -0
- data/lib/workos/types.rb +1 -0
- data/lib/workos/version.rb +1 -1
- data/lib/workos/webhook.rb +47 -0
- data/lib/workos/webhooks.rb +168 -0
- data/lib/workos.rb +3 -0
- data/spec/lib/workos/sso_spec.rb +2 -0
- data/spec/lib/workos/webhooks_spec.rb +190 -0
- data/spec/support/fixtures/vcr_cassettes/organization/create.yml +1 -1
- data/spec/support/fixtures/vcr_cassettes/organization/get.yml +1 -1
- data/spec/support/fixtures/vcr_cassettes/organization/list.yml +4 -4
- data/spec/support/fixtures/vcr_cassettes/organization/update.yml +1 -1
- data/spec/support/fixtures/vcr_cassettes/sso/profile.yml +1 -1
- data/spec/support/profile.txt +1 -1
- data/spec/support/webhook_payload.txt +1 -0
- metadata +10 -5
- data/spec/support/fixtures/vcr_cassettes/organization/update_invalid.yml +0 -73
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dd493c247c202074fa3d93d6f4db6f1cb4cffb19b63f140eeb37a3236255be1a
|
4
|
+
data.tar.gz: a266a358383a5242299a2df002b04486d6ef1203f0d3448736b6d5904d657643
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d05f397238b7e94d6f8e7e513523de96c87da44042c65638dbe8ce947076b5383f38491997a887a30e05a0c47e4193178dd39f0e4c62b5f14ebb1fcfb40b3299
|
7
|
+
data.tar.gz: 28158f92efaff774bbfaaf02e9be76552dd94bfebb996515824e278faf4011b4996846990074e30694f4306ba26c2cb76440294a020e5832b8bf25c3c504ccb3
|
data/.rubocop.yml
CHANGED
data/Gemfile.lock
CHANGED
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
|
data/lib/workos/organization.rb
CHANGED
@@ -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],
|
data/lib/workos/organizations.rb
CHANGED
@@ -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: {
|
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: {
|
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
|
|
data/lib/workos/profile.rb
CHANGED
@@ -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, :
|
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 = T.let(raw.organization_id, String)
|
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],
|
@@ -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
data/lib/workos/version.rb
CHANGED
@@ -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
|
@@ -0,0 +1,168 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
require 'openssl'
|
5
|
+
|
6
|
+
module WorkOS
|
7
|
+
# The Webhooks module provides convenience methods for working with the WorkOS webhooks.
|
8
|
+
# You'll need to extract the signature header and payload from the webhook request
|
9
|
+
# sig_header = request.headers['WorkOS-Signature']
|
10
|
+
# payload = request.body.read
|
11
|
+
#
|
12
|
+
# The secret is the Webhook Secret from your WorkOS Dashboard
|
13
|
+
# The tolerance is for the timestamp validation
|
14
|
+
#
|
15
|
+
module Webhooks
|
16
|
+
class << self
|
17
|
+
extend T::Sig
|
18
|
+
|
19
|
+
DEFAULT_TOLERANCE = 180
|
20
|
+
|
21
|
+
# Initializes an Event object from a JSON payload
|
22
|
+
# rubocop:disable Layout/LineLength
|
23
|
+
#
|
24
|
+
# @param [String] payload The payload from the webhook sent by WorkOS. This is the RAW_POST_DATA of the request.
|
25
|
+
# @param [String] sig_header The signature from the webhook sent by WorkOS.
|
26
|
+
# @param [String] secret The webhook secret from the WorkOS dashboard.
|
27
|
+
# @param [Integer] tolerance The time tolerance in seconds for the webhook.
|
28
|
+
#
|
29
|
+
# @example
|
30
|
+
# WorkOS::Webhooks.construct_event(
|
31
|
+
# payload: "{"id": "wh_123","data":{"id":"directory_user_01FAEAJCR3ZBZ30D8BD1924TVG","state":"active","emails":[{"type":"work","value":"blair@foo-corp.com","primary":true}],"idp_id":"00u1e8mutl6wlH3lL4x7","object":"directory_user","username":"blair@foo-corp.com","last_name":"Lunceford","first_name":"Blair","directory_id":"directory_01F9M7F68PZP8QXP8G7X5QRHS7","raw_attributes":{"name":{"givenName":"Blair","familyName":"Lunceford","middleName":"Elizabeth","honorificPrefix":"Ms."},"title":"Developer Success Engineer","active":true,"emails":[{"type":"work","value":"blair@foo-corp.com","primary":true}],"groups":[],"locale":"en-US","schemas":["urn:ietf:params:scim:schemas:core:2.0:User","urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"],"userName":"blair@foo-corp.com","addresses":[{"region":"CO","primary":true,"locality":"Steamboat Springs","postalCode":"80487"}],"externalId":"00u1e8mutl6wlH3lL4x7","displayName":"Blair Lunceford","urn:ietf:params:scim:schemas:extension:enterprise:2.0:User":{"manager":{"value":"2","displayName":"Kathleen Chung"},"division":"Engineering","department":"Customer Success"}}},"event":"dsync.user.created"}",
|
32
|
+
# sig_header: 't=1626125972272, v1=80f7ab7efadc306eb5797c588cee9410da9be4416782b497bf1e1bf4175fb928',
|
33
|
+
# secret: 'LJlTiC19GmCKWs8AE0IaOQcos',
|
34
|
+
# )
|
35
|
+
#
|
36
|
+
# => #<WorkOS::Webhook:0x00007fa64b980910 @event="dsync.user.created", @data={:id=>"directory_user_01FAEAJCR3ZBZ30D8BD1924TVG", :state=>"active", :emails=>[{:type=>"work", :value=>"blair@foo-corp.com", :primary=>true}], :idp_id=>"00u1e8mutl6wlH3lL4x7", :object=>"directory_user", :username=>"blair@foo-corp.com", :last_name=>"Lunceford", :first_name=>"Blair", :directory_id=>"directory_01F9M7F68PZP8QXP8G7X5QRHS7", :raw_attributes=>{:name=>{:givenName=>"Blair", :familyName=>"Lunceford", :middleName=>"Elizabeth", :honorificPrefix=>"Ms."}, :title=>"Developer Success Engineer", :active=>true, :emails=>[{:type=>"work", :value=>"blair@foo-corp.com", :primary=>true}], :groups=>[], :locale=>"en-US", :schemas=>["urn:ietf:params:scim:schemas:core:2.0:User", "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"], :userName=>"blair@foo-corp.com", :addresses=>[{:region=>"CO", :primary=>true, :locality=>"Steamboat Springs", :postalCode=>"80487"}], :externalId=>"00u1e8mutl6wlH3lL4x7", :displayName=>"Blair Lunceford", :"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"=>{:manager=>{:value=>"2", :displayName=>"Kathleen Chung"}, :division=>"Engineering", :department=>"Customer Success"}}}>
|
37
|
+
#
|
38
|
+
# @return [WorkOS::Webhook]
|
39
|
+
# rubocop:enable Layout/LineLength
|
40
|
+
sig do
|
41
|
+
params(
|
42
|
+
payload: String,
|
43
|
+
sig_header: String,
|
44
|
+
secret: String,
|
45
|
+
tolerance: Integer,
|
46
|
+
).returns(WorkOS::Webhook)
|
47
|
+
end
|
48
|
+
def construct_event(
|
49
|
+
payload:,
|
50
|
+
sig_header:,
|
51
|
+
secret:,
|
52
|
+
tolerance: DEFAULT_TOLERANCE
|
53
|
+
)
|
54
|
+
verify_header(payload: payload, sig_header: sig_header, secret: secret, tolerance: tolerance)
|
55
|
+
WorkOS::Webhook.new(payload)
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
sig do
|
61
|
+
params(
|
62
|
+
payload: String,
|
63
|
+
sig_header: String,
|
64
|
+
secret: String,
|
65
|
+
tolerance: Integer,
|
66
|
+
).returns(T::Boolean)
|
67
|
+
end
|
68
|
+
# rubocop:disable Metrics/MethodLength
|
69
|
+
def verify_header(
|
70
|
+
payload:,
|
71
|
+
sig_header:,
|
72
|
+
secret:,
|
73
|
+
tolerance: DEFAULT_TOLERANCE
|
74
|
+
)
|
75
|
+
begin
|
76
|
+
timestamp, signature_hash = get_timestamp_and_signature_hash(sig_header: sig_header)
|
77
|
+
rescue StandardError
|
78
|
+
raise WorkOS::SignatureVerificationError.new(
|
79
|
+
message: 'Unable to extract timestamp and signature hash from header',
|
80
|
+
)
|
81
|
+
end
|
82
|
+
|
83
|
+
if signature_hash.empty?
|
84
|
+
raise WorkOS::SignatureVerificationError.new(
|
85
|
+
message: 'No signature hash found with expected scheme v1',
|
86
|
+
)
|
87
|
+
end
|
88
|
+
|
89
|
+
if timestamp < Time.now - tolerance
|
90
|
+
raise WorkOS::SignatureVerificationError.new(
|
91
|
+
message: 'Timestamp outside the tolerance zone',
|
92
|
+
)
|
93
|
+
end
|
94
|
+
|
95
|
+
expected_sig = compute_signature(timestamp: timestamp, payload: payload, secret: secret)
|
96
|
+
unless secure_compare(str_a: expected_sig, str_b: signature_hash)
|
97
|
+
raise WorkOS::SignatureVerificationError.new(
|
98
|
+
message: 'Signature hash does not match the expected signature hash for payload',
|
99
|
+
)
|
100
|
+
end
|
101
|
+
|
102
|
+
true
|
103
|
+
end
|
104
|
+
# rubocop:enable Metrics/MethodLength
|
105
|
+
|
106
|
+
sig do
|
107
|
+
params(
|
108
|
+
sig_header: String,
|
109
|
+
).returns(T::Array[T.untyped])
|
110
|
+
end
|
111
|
+
def get_timestamp_and_signature_hash(
|
112
|
+
sig_header:
|
113
|
+
)
|
114
|
+
timestamp, signature_hash = sig_header.split(', ')
|
115
|
+
|
116
|
+
if timestamp.nil? || signature_hash.nil?
|
117
|
+
raise WorkOS::SignatureVerificationError.new(
|
118
|
+
message: 'Unable to extract timestamp and signature hash from header',
|
119
|
+
)
|
120
|
+
end
|
121
|
+
|
122
|
+
timestamp = timestamp.sub('t=', '')
|
123
|
+
signature_hash = signature_hash.sub('v1=', '')
|
124
|
+
|
125
|
+
[Time.at(timestamp.to_i), signature_hash]
|
126
|
+
end
|
127
|
+
|
128
|
+
sig do
|
129
|
+
params(
|
130
|
+
timestamp: Time,
|
131
|
+
payload: String,
|
132
|
+
secret: String,
|
133
|
+
).returns(String)
|
134
|
+
end
|
135
|
+
def compute_signature(
|
136
|
+
timestamp:,
|
137
|
+
payload:,
|
138
|
+
secret:
|
139
|
+
)
|
140
|
+
unhashed_string = "#{timestamp.to_i}.#{payload}"
|
141
|
+
digest = OpenSSL::Digest.new('sha256')
|
142
|
+
OpenSSL::HMAC.hexdigest(digest, secret, unhashed_string)
|
143
|
+
end
|
144
|
+
|
145
|
+
# Constant time string comparison to prevent timing attacks
|
146
|
+
# Code borrowed from ActiveSupport
|
147
|
+
sig do
|
148
|
+
params(
|
149
|
+
str_a: String,
|
150
|
+
str_b: String,
|
151
|
+
).returns(T::Boolean)
|
152
|
+
end
|
153
|
+
def secure_compare(
|
154
|
+
str_a:,
|
155
|
+
str_b:
|
156
|
+
)
|
157
|
+
return false unless str_a.bytesize == str_b.bytesize
|
158
|
+
|
159
|
+
l = T.unsafe(str_a.unpack("C#{str_a.bytesize}"))
|
160
|
+
|
161
|
+
res = 0
|
162
|
+
str_b.each_byte { |byte| res |= byte ^ l.shift }
|
163
|
+
|
164
|
+
res.zero?
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
data/lib/workos.rb
CHANGED
@@ -42,11 +42,14 @@ module WorkOS
|
|
42
42
|
autoload :ProfileAndToken, 'workos/profile_and_token'
|
43
43
|
autoload :SSO, 'workos/sso'
|
44
44
|
autoload :DirectoryUser, 'workos/directory_user'
|
45
|
+
autoload :Webhook, 'workos/webhook'
|
46
|
+
autoload :Webhooks, 'workos/webhooks'
|
45
47
|
|
46
48
|
# Errors
|
47
49
|
autoload :APIError, 'workos/errors'
|
48
50
|
autoload :AuthenticationError, 'workos/errors'
|
49
51
|
autoload :InvalidRequestError, 'workos/errors'
|
52
|
+
autoload :SignatureVerificationError, 'workos/errors'
|
50
53
|
|
51
54
|
# Remove WORKOS_KEY at some point in the future. Keeping it here now for
|
52
55
|
# backwards compatibility.
|
data/spec/lib/workos/sso_spec.rb
CHANGED
@@ -164,6 +164,7 @@ describe WorkOS::SSO do
|
|
164
164
|
id: 'prof_01EEJTY9SZ1R350RB7B73SNBKF',
|
165
165
|
idp_id: '116485463307139932699',
|
166
166
|
last_name: 'Loblaw',
|
167
|
+
organization_id: 'org_01FG53X8636WSNW2WEKB2C31ZB',
|
167
168
|
raw_attributes: {
|
168
169
|
email: 'bob.loblaw@workos.com',
|
169
170
|
family_name: 'Loblaw',
|
@@ -233,6 +234,7 @@ describe WorkOS::SSO do
|
|
233
234
|
id: 'prof_01DRA1XNSJDZ19A31F183ECQW5',
|
234
235
|
idp_id: '00u1klkowm8EGah2H357',
|
235
236
|
last_name: 'Demo',
|
237
|
+
organization_id: 'org_01FG53X8636WSNW2WEKB2C31ZB',
|
236
238
|
raw_attributes: {
|
237
239
|
email: 'demo@workos-okta.com',
|
238
240
|
first_name: 'WorkOS',
|
@@ -0,0 +1,190 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: false
|
3
|
+
|
4
|
+
require 'json'
|
5
|
+
require 'openssl'
|
6
|
+
|
7
|
+
describe WorkOS::Webhooks do
|
8
|
+
describe '.construct_event' do
|
9
|
+
before(:each) do
|
10
|
+
@payload = File.read("#{SPEC_ROOT}/support/webhook_payload.txt")
|
11
|
+
@secret = 'secret'
|
12
|
+
@timestamp = Time.at(Time.now.to_i * 1000)
|
13
|
+
unhashed_string = "#{@timestamp.to_i}.#{@payload}"
|
14
|
+
digest = OpenSSL::Digest.new('sha256')
|
15
|
+
@signature_hash = OpenSSL::HMAC.hexdigest(digest, @secret, unhashed_string)
|
16
|
+
@expectation = {
|
17
|
+
id: 'directory_user_01FAEAJCR3ZBZ30D8BD1924TVG',
|
18
|
+
state: 'active',
|
19
|
+
emails: [{
|
20
|
+
type: 'work',
|
21
|
+
value: 'blair@foo-corp.com',
|
22
|
+
primary: true,
|
23
|
+
}],
|
24
|
+
idp_id: '00u1e8mutl6wlH3lL4x7',
|
25
|
+
object: 'directory_user',
|
26
|
+
username: 'blair@foo-corp.com',
|
27
|
+
last_name: 'Lunceford',
|
28
|
+
first_name: 'Blair',
|
29
|
+
directory_id: 'directory_01F9M7F68PZP8QXP8G7X5QRHS7',
|
30
|
+
raw_attributes: {
|
31
|
+
name: {
|
32
|
+
givenName: 'Blair',
|
33
|
+
familyName: 'Lunceford',
|
34
|
+
middleName: 'Elizabeth',
|
35
|
+
honorificPrefix: 'Ms.',
|
36
|
+
},
|
37
|
+
title: 'Developer Success Engineer',
|
38
|
+
active: true,
|
39
|
+
emails: [{
|
40
|
+
type: 'work',
|
41
|
+
value: 'blair@foo-corp.com',
|
42
|
+
primary: true,
|
43
|
+
}],
|
44
|
+
groups: [],
|
45
|
+
locale: 'en-US',
|
46
|
+
schemas: [
|
47
|
+
'urn:ietf:params:scim:schemas:core:2.0:User',
|
48
|
+
'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User'
|
49
|
+
],
|
50
|
+
userName: 'blair@foo-corp.com',
|
51
|
+
addresses: [{
|
52
|
+
region: 'CO',
|
53
|
+
primary: true,
|
54
|
+
locality: 'Steamboat Springs',
|
55
|
+
postalCode: '80487',
|
56
|
+
}],
|
57
|
+
externalId: '00u1e8mutl6wlH3lL4x7',
|
58
|
+
displayName: 'Blair Lunceford',
|
59
|
+
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": {
|
60
|
+
manager: {
|
61
|
+
value: '2',
|
62
|
+
displayName: 'Kathleen Chung',
|
63
|
+
},
|
64
|
+
division: 'Engineering',
|
65
|
+
department: 'Customer Success',
|
66
|
+
},
|
67
|
+
},
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'with the correct payload, sig_header, and secret' do
|
72
|
+
it 'returns a webhook event' do
|
73
|
+
webhook = described_class.construct_event(
|
74
|
+
payload: @payload,
|
75
|
+
sig_header: "t=#{@timestamp.to_i}, v1=#{@signature_hash}",
|
76
|
+
secret: @secret,
|
77
|
+
)
|
78
|
+
|
79
|
+
expect(webhook.data).to eq(@expectation)
|
80
|
+
expect(webhook.event).to eq('dsync.user.created')
|
81
|
+
expect(webhook.id).to eq('wh_123')
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'with the correct payload, sig_header, secret, and tolerance' do
|
86
|
+
it 'returns a webhook event' do
|
87
|
+
webhook = described_class.construct_event(
|
88
|
+
payload: @payload,
|
89
|
+
sig_header: "t=#{@timestamp.to_i}, v1=#{@signature_hash}",
|
90
|
+
secret: @secret,
|
91
|
+
tolerance: 300,
|
92
|
+
)
|
93
|
+
|
94
|
+
expect(webhook.data).to eq(@expectation)
|
95
|
+
expect(webhook.event).to eq('dsync.user.created')
|
96
|
+
expect(webhook.id).to eq('wh_123')
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context 'with an empty header' do
|
101
|
+
it 'raises an error' do
|
102
|
+
expect do
|
103
|
+
described_class.construct_event(
|
104
|
+
payload: @payload,
|
105
|
+
sig_header: '',
|
106
|
+
secret: @secret,
|
107
|
+
)
|
108
|
+
end.to raise_error(
|
109
|
+
WorkOS::SignatureVerificationError,
|
110
|
+
'Unable to extract timestamp and signature hash from header',
|
111
|
+
)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
context 'with an empty signature hash' do
|
116
|
+
it 'raises an error' do
|
117
|
+
expect do
|
118
|
+
described_class.construct_event(
|
119
|
+
payload: @payload,
|
120
|
+
sig_header: "t=#{@timestamp.to_i}, v1=",
|
121
|
+
secret: @secret,
|
122
|
+
)
|
123
|
+
end.to raise_error(
|
124
|
+
WorkOS::SignatureVerificationError,
|
125
|
+
'No signature hash found with expected scheme v1',
|
126
|
+
)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context 'with an incorrect signature hash' do
|
131
|
+
it 'raises an error' do
|
132
|
+
expect do
|
133
|
+
described_class.construct_event(
|
134
|
+
payload: @payload,
|
135
|
+
sig_header: "t=#{@timestamp.to_i}, v1=99999",
|
136
|
+
secret: @secret,
|
137
|
+
)
|
138
|
+
end.to raise_error(
|
139
|
+
WorkOS::SignatureVerificationError,
|
140
|
+
'Signature hash does not match the expected signature hash for payload',
|
141
|
+
)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
context 'with an incorrect payload' do
|
146
|
+
it 'raises an error' do
|
147
|
+
expect do
|
148
|
+
described_class.construct_event(
|
149
|
+
payload: 'invalid',
|
150
|
+
sig_header: "t=#{@timestamp.to_i}, v1=#{@signature_hash}",
|
151
|
+
secret: @secret,
|
152
|
+
)
|
153
|
+
end.to raise_error(
|
154
|
+
WorkOS::SignatureVerificationError,
|
155
|
+
'Signature hash does not match the expected signature hash for payload',
|
156
|
+
)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
context 'with an incorrect webhook secret' do
|
161
|
+
it 'raises an error' do
|
162
|
+
expect do
|
163
|
+
described_class.construct_event(
|
164
|
+
payload: @payload,
|
165
|
+
sig_header: "t=#{@timestamp.to_i}, v1=#{@signature_hash}",
|
166
|
+
secret: 'invalid',
|
167
|
+
)
|
168
|
+
end.to raise_error(
|
169
|
+
WorkOS::SignatureVerificationError,
|
170
|
+
'Signature hash does not match the expected signature hash for payload',
|
171
|
+
)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
context 'with a timestamp outside tolerance' do
|
176
|
+
it 'raises an error' do
|
177
|
+
expect do
|
178
|
+
described_class.construct_event(
|
179
|
+
payload: @payload,
|
180
|
+
sig_header: "t=9999, v1=#{@signature_hash}",
|
181
|
+
secret: @secret,
|
182
|
+
)
|
183
|
+
end.to raise_error(
|
184
|
+
WorkOS::SignatureVerificationError,
|
185
|
+
'Timestamp outside the tolerance zone',
|
186
|
+
)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
@@ -78,7 +78,7 @@ http_interactions:
|
|
78
78
|
body:
|
79
79
|
encoding: UTF-8
|
80
80
|
string: '{"object":"organization","id":"org_01FCPEJXEZR4DSBA625YMGQT9N","name":"Test
|
81
|
-
Organization","created_at":"2021-08-09T21:55:02.755Z","updated_at":"2021-08-09T21:55:02.755Z","domains":[{"object":"organization_domain","id":"org_domain_01FCPEJXF4CTFEDFEGDTRN1MZG","domain":"example.io"}]}'
|
81
|
+
Organization","allow_profiles_outside_organization":false,"created_at":"2021-08-09T21:55:02.755Z","updated_at":"2021-08-09T21:55:02.755Z","domains":[{"object":"organization_domain","id":"org_domain_01FCPEJXF4CTFEDFEGDTRN1MZG","domain":"example.io"}]}'
|
82
82
|
http_version:
|
83
83
|
recorded_at: Mon, 09 Aug 2021 21:55:02 GMT
|
84
84
|
recorded_with: VCR 5.0.0
|
@@ -78,7 +78,7 @@ http_interactions:
|
|
78
78
|
body:
|
79
79
|
encoding: ASCII-8BIT
|
80
80
|
string: '{"object":"organization","id":"org_01F9293WD2PDEEV4Y625XPZVG7","name":"Foo
|
81
|
-
Corp","created_at":"2021-06-25T19:07:33.155Z","updated_at":"2021-06-25T19:07:33.155Z","domains":[{"object":"organization_domain","id":"org_domain_01F9293WD8KZAS4ESBK57SZ4D8","domain":"foo-corp.com"}]}'
|
81
|
+
Corp","allow_profiles_outside_organization":false,"created_at":"2021-06-25T19:07:33.155Z","updated_at":"2021-06-25T19:07:33.155Z","domains":[{"object":"organization_domain","id":"org_domain_01F9293WD8KZAS4ESBK57SZ4D8","domain":"foo-corp.com"}]}'
|
82
82
|
http_version:
|
83
83
|
recorded_at: Mon, 09 Aug 2021 21:53:34 GMT
|
84
84
|
recorded_with: VCR 5.0.0
|
@@ -78,10 +78,10 @@ http_interactions:
|
|
78
78
|
body:
|
79
79
|
encoding: ASCII-8BIT
|
80
80
|
string: '{"object":"list","data":[{"object":"organization","id":"org_01FCPEJXEZR4DSBA625YMGQT9N","name":"Test
|
81
|
-
Organization","created_at":"2021-08-09T21:55:02.755Z","updated_at":"2021-08-09T21:55:02.755Z","domains":[{"object":"organization_domain","id":"org_domain_01FCPEJXF4CTFEDFEGDTRN1MZG","domain":"example.io"}]},{"object":"organization","id":"org_01F9293WD2PDEEV4Y625XPZVG7","name":"Foo
|
82
|
-
Corp","created_at":"2021-06-25T19:07:33.155Z","updated_at":"2021-06-25T19:07:33.155Z","domains":[{"object":"organization_domain","id":"org_domain_01F9293WD8KZAS4ESBK57SZ4D8","domain":"foo-corp.com"}]},{"object":"organization","id":"org_01F8873JSZWN1MJCDN537FEK1H","name":"Example
|
83
|
-
Organization","created_at":"2021-06-15T16:12:10.942Z","updated_at":"2021-06-15T16:12:10.942Z","domains":[]},{"object":"organization","id":"org_01F79Z8TGGTA8Q67Q50FDXAYN3","name":"gmail.com","created_at":"2021-06-03T22:18:01.100Z","updated_at":"2021-06-03T22:18:01.100Z","domains":[{"object":"organization_domain","id":"org_domain_01F79Z8TGS4VYFX0246RAWSZMP","domain":"gmail.com"}]},{"object":"organization","id":"org_01F6Q6TFP7RD2PF6J03ANNWDKV","name":"Test
|
84
|
-
Organization","created_at":"2021-05-27T15:24:25.670Z","updated_at":"2021-08-09T21:53:34.525Z","domains":[{"object":"organization_domain","id":"org_domain_01FCPEG7BAYMQ4CHMG41Y2VNHF","domain":"example.me"}]},{"object":"organization","id":"org_01F6Q6NGZS8R5ZTH3C1XR96JK7","name":"WorkOS","created_at":"2021-05-27T15:21:43.160Z","updated_at":"2021-05-27T15:21:43.160Z","domains":[{"object":"organization_domain","id":"org_domain_01F6Q6NGZZ54WSX1XCMCKSC4DA","domain":"workos.com"}]}],"listMetadata":{"before":"before-id","after":null}}'
|
81
|
+
Organization","allow_profiles_outside_organization":false,"created_at":"2021-08-09T21:55:02.755Z","updated_at":"2021-08-09T21:55:02.755Z","domains":[{"object":"organization_domain","id":"org_domain_01FCPEJXF4CTFEDFEGDTRN1MZG","domain":"example.io"}]},{"object":"organization","id":"org_01F9293WD2PDEEV4Y625XPZVG7","name":"Foo
|
82
|
+
Corp","allow_profiles_outside_organization":false,"created_at":"2021-06-25T19:07:33.155Z","updated_at":"2021-06-25T19:07:33.155Z","domains":[{"object":"organization_domain","id":"org_domain_01F9293WD8KZAS4ESBK57SZ4D8","domain":"foo-corp.com"}]},{"object":"organization","id":"org_01F8873JSZWN1MJCDN537FEK1H","name":"Example
|
83
|
+
Organization","allow_profiles_outside_organization":false,"created_at":"2021-06-15T16:12:10.942Z","updated_at":"2021-06-15T16:12:10.942Z","domains":[]},{"object":"organization","id":"org_01F79Z8TGGTA8Q67Q50FDXAYN3","name":"gmail.com","allow_profiles_outside_organization":false,"created_at":"2021-06-03T22:18:01.100Z","updated_at":"2021-06-03T22:18:01.100Z","domains":[{"object":"organization_domain","id":"org_domain_01F79Z8TGS4VYFX0246RAWSZMP","domain":"gmail.com"}]},{"object":"organization","id":"org_01F6Q6TFP7RD2PF6J03ANNWDKV","name":"Test
|
84
|
+
Organization","allow_profiles_outside_organization":false,"created_at":"2021-05-27T15:24:25.670Z","updated_at":"2021-08-09T21:53:34.525Z","domains":[{"object":"organization_domain","id":"org_domain_01FCPEG7BAYMQ4CHMG41Y2VNHF","domain":"example.me"}]},{"object":"organization","id":"org_01F6Q6NGZS8R5ZTH3C1XR96JK7","name":"WorkOS","allow_profiles_outside_organization":false,"created_at":"2021-05-27T15:21:43.160Z","updated_at":"2021-05-27T15:21:43.160Z","domains":[{"object":"organization_domain","id":"org_domain_01F6Q6NGZZ54WSX1XCMCKSC4DA","domain":"workos.com"}]}],"listMetadata":{"before":"before-id","after":null}}'
|
85
85
|
http_version:
|
86
86
|
recorded_at: Mon, 09 Aug 2021 21:55:03 GMT
|
87
87
|
recorded_with: VCR 5.0.0
|
@@ -78,7 +78,7 @@ http_interactions:
|
|
78
78
|
body:
|
79
79
|
encoding: ASCII-8BIT
|
80
80
|
string: '{"object":"organization","id":"org_01F6Q6TFP7RD2PF6J03ANNWDKV","name":"Test
|
81
|
-
Organization","created_at":"2021-05-27T15:24:25.670Z","updated_at":"2021-08-09T21:53:34.525Z","domains":[{"object":"organization_domain","id":"org_domain_01FCPEG7BAYMQ4CHMG41Y2VNHF","domain":"example.me"}]}'
|
81
|
+
Organization","allow_profiles_outside_organization":false,"created_at":"2021-05-27T15:24:25.670Z","updated_at":"2021-08-09T21:53:34.525Z","domains":[{"object":"organization_domain","id":"org_domain_01FCPEG7BAYMQ4CHMG41Y2VNHF","domain":"example.me"}]}'
|
82
82
|
http_version:
|
83
83
|
recorded_at: Mon, 09 Aug 2021 21:53:34 GMT
|
84
84
|
recorded_with: VCR 5.0.0
|
@@ -67,7 +67,7 @@ http_interactions:
|
|
67
67
|
body:
|
68
68
|
encoding: UTF-8
|
69
69
|
string:
|
70
|
-
'{"object":"profile","id":"prof_01EEJTY9SZ1R350RB7B73SNBKF","connection_id":"conn_01E83FVYZHY7DM4S9503JHV0R5","connection_type":"GoogleOAuth","idp_id":"116485463307139932699","email":"bob.loblaw@workos.com","first_name":"Bob","last_name":"Loblaw","raw_attributes":{"hd":"workos.com","id":"116485463307139932699","name":"Bob
|
70
|
+
'{"object":"profile","id":"prof_01EEJTY9SZ1R350RB7B73SNBKF","organization_id":"org_01FG53X8636WSNW2WEKB2C31ZB","connection_id":"conn_01E83FVYZHY7DM4S9503JHV0R5","connection_type":"GoogleOAuth","idp_id":"116485463307139932699","email":"bob.loblaw@workos.com","first_name":"Bob","last_name":"Loblaw","raw_attributes":{"hd":"workos.com","id":"116485463307139932699","name":"Bob
|
71
71
|
Loblaw","email":"bob.loblaw@workos.com","locale":"en","picture":"https://lh3.googleusercontent.com/a-/AOh14GyO2hLlgZvteDQ3Ldi3_-RteZLya0hWH7247Cam=s96-c","given_name":"Bob","family_name":"Loblaw","verified_email":true}}'
|
72
72
|
http_version:
|
73
73
|
recorded_at: Tue, 18 May 2021 22:55:21 GMT
|
data/spec/support/profile.txt
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"profile":{"object":"profile","id":"prof_01DRA1XNSJDZ19A31F183ECQW5","email":"demo@workos-okta.com","first_name":"WorkOS","connection_id":"conn_01EMH8WAK20T42N2NBMNBCYHAG","connection_type":"OktaSAML","last_name":"Demo","idp_id":"00u1klkowm8EGah2H357","raw_attributes":{"id":"prof_01DRA1XNSJDZ19A31F183ECQW5","email":"demo@workos-okta.com","first_name":"WorkOS","last_name":"Demo","idp_id":"00u1klkowm8EGah2H357"}},"access_token":"01DVX6QBS3EG6FHY2ESAA5Q65X"}
|
1
|
+
{"profile":{"object":"profile","id":"prof_01DRA1XNSJDZ19A31F183ECQW5","email":"demo@workos-okta.com","first_name":"WorkOS","organization_id":"org_01FG53X8636WSNW2WEKB2C31ZB","connection_id":"conn_01EMH8WAK20T42N2NBMNBCYHAG","connection_type":"OktaSAML","last_name":"Demo","idp_id":"00u1klkowm8EGah2H357","raw_attributes":{"id":"prof_01DRA1XNSJDZ19A31F183ECQW5","email":"demo@workos-okta.com","first_name":"WorkOS","last_name":"Demo","idp_id":"00u1klkowm8EGah2H357"}},"access_token":"01DVX6QBS3EG6FHY2ESAA5Q65X"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"id": "wh_123","data":{"id":"directory_user_01FAEAJCR3ZBZ30D8BD1924TVG","state":"active","emails":[{"type":"work","value":"blair@foo-corp.com","primary":true}],"idp_id":"00u1e8mutl6wlH3lL4x7","object":"directory_user","username":"blair@foo-corp.com","last_name":"Lunceford","first_name":"Blair","directory_id":"directory_01F9M7F68PZP8QXP8G7X5QRHS7","raw_attributes":{"name":{"givenName":"Blair","familyName":"Lunceford","middleName":"Elizabeth","honorificPrefix":"Ms."},"title":"Developer Success Engineer","active":true,"emails":[{"type":"work","value":"blair@foo-corp.com","primary":true}],"groups":[],"locale":"en-US","schemas":["urn:ietf:params:scim:schemas:core:2.0:User","urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"],"userName":"blair@foo-corp.com","addresses":[{"region":"CO","primary":true,"locality":"Steamboat Springs","postalCode":"80487"}],"externalId":"00u1e8mutl6wlH3lL4x7","displayName":"Blair Lunceford","urn:ietf:params:scim:schemas:extension:enterprise:2.0:User":{"manager":{"value":"2","displayName":"Kathleen Chung"},"division":"Engineering","department":"Customer Success"}}},"event":"dsync.user.created"}
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: workos
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- WorkOS
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-09-
|
11
|
+
date: 2021-09-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sorbet-runtime
|
@@ -234,7 +234,10 @@ files:
|
|
234
234
|
- lib/workos/types/passwordless_session_struct.rb
|
235
235
|
- lib/workos/types/profile_struct.rb
|
236
236
|
- lib/workos/types/provider_enum.rb
|
237
|
+
- lib/workos/types/webhook_struct.rb
|
237
238
|
- lib/workos/version.rb
|
239
|
+
- lib/workos/webhook.rb
|
240
|
+
- lib/workos/webhooks.rb
|
238
241
|
- sorbet/config
|
239
242
|
- sorbet/rbi/gems/addressable.rbi
|
240
243
|
- sorbet/rbi/gems/ast.rbi
|
@@ -276,6 +279,7 @@ files:
|
|
276
279
|
- spec/lib/workos/passwordless_spec.rb
|
277
280
|
- spec/lib/workos/portal_spec.rb
|
278
281
|
- spec/lib/workos/sso_spec.rb
|
282
|
+
- spec/lib/workos/webhooks_spec.rb
|
279
283
|
- spec/spec_helper.rb
|
280
284
|
- spec/support/fixtures/vcr_cassettes/audit_trail/create_event.yml
|
281
285
|
- spec/support/fixtures/vcr_cassettes/audit_trail/create_event_custom_idempotency_key.yml
|
@@ -315,7 +319,6 @@ files:
|
|
315
319
|
- spec/support/fixtures/vcr_cassettes/organization/get_invalid.yml
|
316
320
|
- spec/support/fixtures/vcr_cassettes/organization/list.yml
|
317
321
|
- spec/support/fixtures/vcr_cassettes/organization/update.yml
|
318
|
-
- spec/support/fixtures/vcr_cassettes/organization/update_invalid.yml
|
319
322
|
- spec/support/fixtures/vcr_cassettes/passwordless/create_session.yml
|
320
323
|
- spec/support/fixtures/vcr_cassettes/passwordless/create_session_invalid.yml
|
321
324
|
- spec/support/fixtures/vcr_cassettes/passwordless/send_session.yml
|
@@ -337,6 +340,7 @@ files:
|
|
337
340
|
- spec/support/fixtures/vcr_cassettes/sso/profile.yml
|
338
341
|
- spec/support/profile.txt
|
339
342
|
- spec/support/shared_examples/client_spec.rb
|
343
|
+
- spec/support/webhook_payload.txt
|
340
344
|
- workos.gemspec
|
341
345
|
homepage: https://github.com/workos-inc/workos-ruby
|
342
346
|
licenses:
|
@@ -358,7 +362,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
358
362
|
- !ruby/object:Gem::Version
|
359
363
|
version: '0'
|
360
364
|
requirements: []
|
361
|
-
rubygems_version: 3.2.
|
365
|
+
rubygems_version: 3.2.27
|
362
366
|
signing_key:
|
363
367
|
specification_version: 4
|
364
368
|
summary: API client for WorkOS
|
@@ -370,6 +374,7 @@ test_files:
|
|
370
374
|
- spec/lib/workos/passwordless_spec.rb
|
371
375
|
- spec/lib/workos/portal_spec.rb
|
372
376
|
- spec/lib/workos/sso_spec.rb
|
377
|
+
- spec/lib/workos/webhooks_spec.rb
|
373
378
|
- spec/spec_helper.rb
|
374
379
|
- spec/support/fixtures/vcr_cassettes/audit_trail/create_event.yml
|
375
380
|
- spec/support/fixtures/vcr_cassettes/audit_trail/create_event_custom_idempotency_key.yml
|
@@ -409,7 +414,6 @@ test_files:
|
|
409
414
|
- spec/support/fixtures/vcr_cassettes/organization/get_invalid.yml
|
410
415
|
- spec/support/fixtures/vcr_cassettes/organization/list.yml
|
411
416
|
- spec/support/fixtures/vcr_cassettes/organization/update.yml
|
412
|
-
- spec/support/fixtures/vcr_cassettes/organization/update_invalid.yml
|
413
417
|
- spec/support/fixtures/vcr_cassettes/passwordless/create_session.yml
|
414
418
|
- spec/support/fixtures/vcr_cassettes/passwordless/create_session_invalid.yml
|
415
419
|
- spec/support/fixtures/vcr_cassettes/passwordless/send_session.yml
|
@@ -431,3 +435,4 @@ test_files:
|
|
431
435
|
- spec/support/fixtures/vcr_cassettes/sso/profile.yml
|
432
436
|
- spec/support/profile.txt
|
433
437
|
- spec/support/shared_examples/client_spec.rb
|
438
|
+
- spec/support/webhook_payload.txt
|
@@ -1,73 +0,0 @@
|
|
1
|
-
---
|
2
|
-
http_interactions:
|
3
|
-
- request:
|
4
|
-
method: put
|
5
|
-
uri: https://api.workos.com/organizations/org_01F29YJ068E52HGEB8ZQGC9MJG
|
6
|
-
body:
|
7
|
-
encoding: UTF-8
|
8
|
-
string: '{"domains":["example.com"],"name":"Test Organization 2"}'
|
9
|
-
headers:
|
10
|
-
Content-Type:
|
11
|
-
- application/json
|
12
|
-
Accept-Encoding:
|
13
|
-
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
14
|
-
Accept:
|
15
|
-
- "*/*"
|
16
|
-
User-Agent:
|
17
|
-
- WorkOS; ruby/3.0.1; x86_64-darwin19; v0.11.1
|
18
|
-
Authorization:
|
19
|
-
- Bearer <API_KEY>
|
20
|
-
response:
|
21
|
-
status:
|
22
|
-
code: 200
|
23
|
-
message: OK
|
24
|
-
headers:
|
25
|
-
Server:
|
26
|
-
- Cowboy
|
27
|
-
Connection:
|
28
|
-
- keep-alive
|
29
|
-
Vary:
|
30
|
-
- Origin, Accept-Encoding
|
31
|
-
Access-Control-Allow-Credentials:
|
32
|
-
- 'true'
|
33
|
-
Content-Security-Policy:
|
34
|
-
- 'default-src ''self'';base-uri ''self'';block-all-mixed-content;font-src ''self''
|
35
|
-
https: data:;frame-ancestors ''self'';img-src ''self'' data:;object-src ''none'';script-src
|
36
|
-
''self'';script-src-attr ''none'';style-src ''self'' https: ''unsafe-inline'';upgrade-insecure-requests'
|
37
|
-
X-Dns-Prefetch-Control:
|
38
|
-
- 'off'
|
39
|
-
Expect-Ct:
|
40
|
-
- max-age=0
|
41
|
-
X-Frame-Options:
|
42
|
-
- SAMEORIGIN
|
43
|
-
Strict-Transport-Security:
|
44
|
-
- max-age=15552000; includeSubDomains
|
45
|
-
X-Download-Options:
|
46
|
-
- noopen
|
47
|
-
X-Content-Type-Options:
|
48
|
-
- nosniff
|
49
|
-
X-Permitted-Cross-Domain-Policies:
|
50
|
-
- none
|
51
|
-
Referrer-Policy:
|
52
|
-
- no-referrer
|
53
|
-
X-Xss-Protection:
|
54
|
-
- '0'
|
55
|
-
X-Request-Id:
|
56
|
-
- 6ba2cbf9-76e3-4a6a-8ebe-d25edb366ed0
|
57
|
-
Content-Type:
|
58
|
-
- application/json; charset=utf-8
|
59
|
-
Content-Length:
|
60
|
-
- '205'
|
61
|
-
Etag:
|
62
|
-
- W/"cd-mlRMlyyz/LtzInRhAghs1B+BT4s"
|
63
|
-
Date:
|
64
|
-
- Wed, 05 May 2021 22:14:10 GMT
|
65
|
-
Via:
|
66
|
-
- 1.1 vegur
|
67
|
-
body:
|
68
|
-
encoding: UTF-8
|
69
|
-
string: '{"object":"organization","id":"org_01F29YJ068E52HGEB8ZQGC9MJG","name":"Test
|
70
|
-
Organization 2","domains":[{"object":"organization_domain","id":"org_domain_01F4Z9GY3089GV4SENXWZ9RBX1","domain":"example.com"}]}'
|
71
|
-
http_version:
|
72
|
-
recorded_at: Wed, 05 May 2021 22:14:10 GMT
|
73
|
-
recorded_with: VCR 5.0.0
|