workos 2.1.0 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.semaphore/semaphore.yml +13 -39
  3. data/Gemfile.lock +2 -2
  4. data/README.md +4 -0
  5. data/lib/workos/challenge.rb +55 -0
  6. data/lib/workos/client.rb +2 -0
  7. data/lib/workos/connection.rb +1 -0
  8. data/lib/workos/deprecated_hash_wrapper.rb +76 -0
  9. data/lib/workos/directory.rb +1 -0
  10. data/lib/workos/directory_group.rb +5 -2
  11. data/lib/workos/directory_sync.rb +9 -8
  12. data/lib/workos/directory_user.rb +4 -1
  13. data/lib/workos/errors.rb +3 -1
  14. data/lib/workos/factor.rb +59 -0
  15. data/lib/workos/hash_provider.rb +19 -0
  16. data/lib/workos/mfa.rb +165 -0
  17. data/lib/workos/organization.rb +1 -0
  18. data/lib/workos/profile.rb +1 -0
  19. data/lib/workos/profile_and_token.rb +1 -0
  20. data/lib/workos/types/challenge_struct.rb +18 -0
  21. data/lib/workos/types/factor_struct.rb +19 -0
  22. data/lib/workos/types/passwordless_session_struct.rb +2 -0
  23. data/lib/workos/types/verify_factor_struct.rb +13 -0
  24. data/lib/workos/types.rb +3 -0
  25. data/lib/workos/verify_factor.rb +40 -0
  26. data/lib/workos/version.rb +1 -1
  27. data/lib/workos/webhook.rb +1 -0
  28. data/lib/workos/webhooks.rb +9 -7
  29. data/lib/workos.rb +7 -0
  30. data/spec/lib/workos/directory_sync_spec.rb +4 -0
  31. data/spec/lib/workos/mfa_spec.rb +262 -0
  32. data/spec/lib/workos/sso_spec.rb +0 -2
  33. data/spec/lib/workos/webhooks_spec.rb +1 -1
  34. data/spec/support/fixtures/vcr_cassettes/mfa/challenge_factor_generic_valid.yml +82 -0
  35. data/spec/support/fixtures/vcr_cassettes/mfa/challenge_factor_sms_valid.yml +82 -0
  36. data/spec/support/fixtures/vcr_cassettes/mfa/challenge_factor_totp_valid.yml +82 -0
  37. data/spec/support/fixtures/vcr_cassettes/mfa/delete_factor.yml +80 -0
  38. data/spec/support/fixtures/vcr_cassettes/mfa/enroll_factor_generic_valid.yml +82 -0
  39. data/spec/support/fixtures/vcr_cassettes/mfa/enroll_factor_sms_valid.yml +82 -0
  40. data/spec/support/fixtures/vcr_cassettes/mfa/enroll_factor_totp_valid.yml +82 -0
  41. data/spec/support/fixtures/vcr_cassettes/mfa/get_factor_invalid.yml +82 -0
  42. data/spec/support/fixtures/vcr_cassettes/mfa/get_factor_valid.yml +82 -0
  43. data/spec/support/fixtures/vcr_cassettes/mfa/verify_factor_generic_expired.yml +84 -0
  44. data/spec/support/fixtures/vcr_cassettes/mfa/verify_factor_generic_invalid.yml +84 -0
  45. data/spec/support/fixtures/vcr_cassettes/mfa/verify_factor_generic_valid.yml +82 -0
  46. data/spec/support/fixtures/vcr_cassettes/mfa/verify_factor_generic_valid_is_false.yml +82 -0
  47. metadata +40 -3
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+ # typed: strict
3
+
4
+ module WorkOS
5
+ module Types
6
+ # This FactorStruct acts as a typed interface
7
+ # for the Factor class
8
+ class FactorStruct < T::Struct
9
+ const :id, String
10
+ const :environment_id, String
11
+ const :object, String
12
+ const :type, String
13
+ const :totp, T.nilable(T::Hash[Symbol, Object])
14
+ const :sms, T.nilable(T::Hash[Symbol, Object])
15
+ const :created_at, String
16
+ const :updated_at, String
17
+ end
18
+ end
19
+ end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
  # typed: strict
3
3
 
4
+ require 'date'
5
+
4
6
  module WorkOS
5
7
  module Types
6
8
  # This PasswordlessSessionStruct acts as a typed interface
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+ # typed: strict
3
+
4
+ module WorkOS
5
+ module Types
6
+ # This VerifyFactorStruct acts as a typed interface
7
+ # for the Factor class
8
+ class VerifyFactorStruct < T::Struct
9
+ const :challenge, T.nilable(T::Hash[Symbol, Object])
10
+ const :valid, T::Boolean
11
+ end
12
+ end
13
+ end
data/lib/workos/types.rb CHANGED
@@ -16,5 +16,8 @@ module WorkOS
16
16
  require_relative 'types/provider_enum'
17
17
  require_relative 'types/directory_user_struct'
18
18
  require_relative 'types/webhook_struct'
19
+ require_relative 'types/factor_struct'
20
+ require_relative 'types/challenge_struct'
21
+ require_relative 'types/verify_factor_struct'
19
22
  end
20
23
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+ # typed: false
3
+
4
+ module WorkOS
5
+ # The VerifyFactor class provides a lightweight wrapper around
6
+ # a WorkOS DirectoryUser resource. This class is not meant to be instantiated
7
+ # in DirectoryUser space, and is instantiated internally but exposed.
8
+ class VerifyFactor
9
+ include HashProvider
10
+ extend T::Sig
11
+
12
+ attr_accessor :challenge, :valid
13
+
14
+ sig { params(json: String).void }
15
+ def initialize(json)
16
+ raw = parse_json(json)
17
+ @challenge = T.let(raw.challenge, Hash)
18
+ @valid = raw.valid
19
+ end
20
+
21
+ def to_json(*)
22
+ {
23
+ challenge: challenge,
24
+ valid: valid,
25
+ }
26
+ end
27
+
28
+ private
29
+
30
+ sig { params(json_string: String).returns(WorkOS::Types::VerifyFactorStruct) }
31
+ def parse_json(json_string)
32
+ hash = JSON.parse(json_string, symbolize_names: true)
33
+
34
+ WorkOS::Types::VerifyFactorStruct.new(
35
+ challenge: hash[:challenge],
36
+ valid: hash[:valid],
37
+ )
38
+ end
39
+ end
40
+ end
@@ -2,5 +2,5 @@
2
2
  # typed: strong
3
3
 
4
4
  module WorkOS
5
- VERSION = '2.1.0'
5
+ VERSION = '2.2.1'
6
6
  end
@@ -6,6 +6,7 @@ module WorkOS
6
6
  # a WorkOS Webhook resource. This class is not meant to be instantiated
7
7
  # in user space, and is instantiated internally but exposed.
8
8
  class Webhook
9
+ include HashProvider
9
10
  extend T::Sig
10
11
 
11
12
  attr_accessor :id, :event, :data
@@ -65,7 +65,7 @@ module WorkOS
65
65
  tolerance: Integer,
66
66
  ).returns(T::Boolean)
67
67
  end
68
- # rubocop:disable Metrics/MethodLength
68
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
69
69
  def verify_header(
70
70
  payload:,
71
71
  sig_header:,
@@ -86,7 +86,9 @@ module WorkOS
86
86
  )
87
87
  end
88
88
 
89
- if timestamp < Time.now - tolerance
89
+ timestamp_to_time = Time.at(timestamp.to_i / 1000)
90
+
91
+ if timestamp_to_time < Time.now - tolerance
90
92
  raise WorkOS::SignatureVerificationError.new(
91
93
  message: 'Timestamp outside the tolerance zone',
92
94
  )
@@ -101,12 +103,12 @@ module WorkOS
101
103
 
102
104
  true
103
105
  end
104
- # rubocop:enable Metrics/MethodLength
106
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
105
107
 
106
108
  sig do
107
109
  params(
108
110
  sig_header: String,
109
- ).returns(T::Array[T.untyped])
111
+ ).returns([String, String])
110
112
  end
111
113
  def get_timestamp_and_signature_hash(
112
114
  sig_header:
@@ -122,12 +124,12 @@ module WorkOS
122
124
  timestamp = timestamp.sub('t=', '')
123
125
  signature_hash = signature_hash.sub('v1=', '')
124
126
 
125
- [Time.at(timestamp.to_i), signature_hash]
127
+ [timestamp, signature_hash]
126
128
  end
127
129
 
128
130
  sig do
129
131
  params(
130
- timestamp: Time,
132
+ timestamp: String,
131
133
  payload: String,
132
134
  secret: String,
133
135
  ).returns(String)
@@ -137,7 +139,7 @@ module WorkOS
137
139
  payload:,
138
140
  secret:
139
141
  )
140
- unhashed_string = "#{timestamp.to_i}.#{payload}"
142
+ unhashed_string = "#{timestamp}.#{payload}"
141
143
  digest = OpenSSL::Digest.new('sha256')
142
144
  OpenSSL::HMAC.hexdigest(digest, secret, unhashed_string)
143
145
  end
data/lib/workos.rb CHANGED
@@ -4,6 +4,7 @@
4
4
  require 'workos/version'
5
5
  require 'sorbet-runtime'
6
6
  require 'json'
7
+ require 'workos/hash_provider'
7
8
 
8
9
  # Use the WorkOS module to authenticate your
9
10
  # requests to the WorkOS API. The gem will read
@@ -44,6 +45,12 @@ module WorkOS
44
45
  autoload :DirectoryUser, 'workos/directory_user'
45
46
  autoload :Webhook, 'workos/webhook'
46
47
  autoload :Webhooks, 'workos/webhooks'
48
+ autoload :MFA, 'workos/mfa'
49
+ autoload :Factor, 'workos/factor'
50
+ autoload :Challenge, 'workos/challenge'
51
+ autoload :VerifyFactor, 'workos/verify_factor'
52
+ autoload :DeprecatedHashWrapper, 'workos/deprecated_hash_wrapper'
53
+
47
54
 
48
55
  # Errors
49
56
  autoload :APIError, 'workos/errors'
@@ -204,6 +204,7 @@ describe WorkOS::DirectorySync do
204
204
  )
205
205
 
206
206
  expect(groups.data.size).to eq(10)
207
+ expect(groups.data[0].name).to eq(groups.data[0]['name'])
207
208
  end
208
209
  end
209
210
  end
@@ -332,6 +333,7 @@ describe WorkOS::DirectorySync do
332
333
  )
333
334
 
334
335
  expect(users.data.size).to eq(4)
336
+ expect(users.data[0].first_name).to eq(users.data[0]['first_name'])
335
337
  end
336
338
  end
337
339
  end
@@ -440,6 +442,7 @@ describe WorkOS::DirectorySync do
440
442
  )
441
443
 
442
444
  expect(group['name']).to eq('Walrus')
445
+ expect(group.name).to eq('Walrus')
443
446
  end
444
447
  end
445
448
  end
@@ -464,6 +467,7 @@ describe WorkOS::DirectorySync do
464
467
  )
465
468
 
466
469
  expect(user['first_name']).to eq('Logan')
470
+ expect(user.first_name).to eq('Logan')
467
471
  end
468
472
  end
469
473
  end
@@ -0,0 +1,262 @@
1
+ # frozen_string_literal: true
2
+ # typed: false
3
+
4
+ describe WorkOS::MFA do
5
+ it_behaves_like 'client'
6
+
7
+ describe 'enroll_factor valid requests' do
8
+ context 'enroll factor using valid generic argument' do
9
+ it 'returns a valid factor object' do
10
+ VCR.use_cassette 'mfa/enroll_factor_generic_valid' do
11
+ factor = described_class.enroll_factor(
12
+ type: 'generic_otp',
13
+ )
14
+ expect(factor.type == 'generic_otp')
15
+ end
16
+ end
17
+ end
18
+
19
+ context 'enroll factor using valid totp arguments' do
20
+ it 'returns a valid factor object' do
21
+ VCR.use_cassette 'mfa/enroll_factor_totp_valid' do
22
+ factor = described_class.enroll_factor(
23
+ type: 'totp',
24
+ totp_issuer: 'WorkOS',
25
+ totp_user: 'some_user',
26
+ )
27
+ expect(factor.totp.instance_of?(Hash))
28
+ end
29
+ end
30
+ end
31
+
32
+ context 'enroll factor using valid sms arguments' do
33
+ it 'returns a valid factor object' do
34
+ VCR.use_cassette 'mfa/enroll_factor_sms_valid' do
35
+ factor = described_class.enroll_factor(
36
+ type: 'sms',
37
+ phone_number: '55555555555',
38
+ )
39
+ expect(factor.sms.instance_of?(Hash))
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ describe 'enroll_factor invalid responses' do
46
+ context 'enroll factor throws error if type is not sms or totp' do
47
+ it 'returns an error' do
48
+ expect do
49
+ described_class.enroll_factor(
50
+ type: 'invalid',
51
+ phone_number: '+15005550006',
52
+ )
53
+ end.to raise_error(
54
+ ArgumentError,
55
+ "Type argument must be either 'sms' or 'totp'",
56
+ )
57
+ end
58
+ end
59
+
60
+ context 'enroll factor throws error if type is not sms or totp' do
61
+ it 'returns an error' do
62
+ expect do
63
+ described_class.enroll_factor(
64
+ type: 'totp',
65
+ totp_issuer: 'WorkOS',
66
+ )
67
+ end.to raise_error(
68
+ ArgumentError,
69
+ 'Incomplete arguments. Need to specify both totp_issuer and totp_user when type is totp',
70
+ )
71
+ end
72
+ end
73
+ context 'enroll factor throws error if type sms and phone number is nil' do
74
+ it 'returns an error' do
75
+ expect do
76
+ described_class.enroll_factor(
77
+ type: 'sms',
78
+ )
79
+ end.to raise_error(
80
+ ArgumentError,
81
+ 'Incomplete arguments. Need to specify phone_number when type is sms',
82
+ )
83
+ end
84
+ end
85
+ end
86
+
87
+ describe 'challenge factor with valid request arguments' do
88
+ context 'challenge with totp' do
89
+ it 'returns challenge factor object for totp' do
90
+ VCR.use_cassette 'mfa/challenge_factor_totp_valid' do
91
+ challenge_factor = described_class.challenge_factor(
92
+ authentication_factor_id: 'auth_factor_01FZ4TS0MWPZR7GATS7KCXANQZ',
93
+ )
94
+ expect(challenge_factor.authentication_factor_id.class.instance_of?(String))
95
+ end
96
+ end
97
+ end
98
+
99
+ context 'challenge with sms' do
100
+ it 'returns a challenge factor object for sms' do
101
+ VCR.use_cassette 'mfa/challenge_factor_sms_valid' do
102
+ challenge_factor = described_class.challenge_factor(
103
+ authentication_factor_id: 'auth_factor_01FZ4TS14D1PHFNZ9GF6YD8M1F',
104
+ sms_template: 'Your code is {{code}}',
105
+ )
106
+ expect(challenge_factor.authentication_factor_id.instance_of?(String))
107
+ end
108
+ end
109
+ end
110
+
111
+ context 'challenge with generic' do
112
+ it 'returns a valid challenge factor object for generic otp' do
113
+ VCR.use_cassette 'mfa/challenge_factor_generic_valid' do
114
+ challenge_factor = described_class.challenge_factor(
115
+ authentication_factor_id: 'auth_factor_01FZ4WMXXA09XF6NK1XMKNWB3M',
116
+ )
117
+ expect(challenge_factor.code.instance_of?(String))
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ describe 'challenge factor with invalid arguments' do
124
+ context 'challenge with totp mssing authentication_factor_id' do
125
+ it 'returns argument error' do
126
+ expect do
127
+ described_class.challenge_factor
128
+ end.to raise_error(
129
+ ArgumentError,
130
+ "Incomplete arguments: 'authentication_factor_id' is a required argument",
131
+ )
132
+ end
133
+ end
134
+ end
135
+
136
+ describe 'challenge factor with valid requests' do
137
+ context 'verify generic otp' do
138
+ it 'returns a true boolean if the challenge has not been verifed yet' do
139
+ VCR.use_cassette 'mfa/verify_factor_generic_valid' do
140
+ verify_factor = described_class.verify_factor(
141
+ authentication_challenge_id: 'auth_challenge_01FZ4YVRBMXP5ZM0A7BP4AJ12J',
142
+ code: '897792',
143
+ )
144
+ expect(verify_factor.valid == 'true')
145
+ end
146
+ end
147
+ end
148
+
149
+ context 'verify generic otp invalid response' do
150
+ it 'returns a true boolean if the challenge has not been verifed yet' do
151
+ VCR.use_cassette 'mfa/verify_factor_generic_valid_is_false' do
152
+ verify_factor = described_class.verify_factor(
153
+ authentication_challenge_id: 'auth_challenge_01FZ4YVRBMXP5ZM0A7BP4AJ12J',
154
+ code: '897792',
155
+ )
156
+ expect(verify_factor.valid == 'false')
157
+ end
158
+ end
159
+ end
160
+
161
+ context 'verify generic otp' do
162
+ it 'returns error that the challenge has already been verfied' do
163
+ VCR.use_cassette 'mfa/verify_factor_generic_invalid' do
164
+ expect do
165
+ described_class.verify_factor(
166
+ authentication_challenge_id: 'auth_challenge_01FZ4YVRBMXP5ZM0A7BP4AJ12J',
167
+ code: '897792',
168
+ )
169
+ end.to raise_error(WorkOS::InvalidRequestError)
170
+ end
171
+ end
172
+
173
+ context 'verify generic otp' do
174
+ it 'returns error that the challenge has expired' do
175
+ VCR.use_cassette 'mfa/verify_factor_generic_expired' do
176
+ expect do
177
+ described_class.verify_factor(
178
+ authentication_challenge_id: 'auth_challenge_01FZ4YVRBMXP5ZM0A7BP4AJ12J',
179
+ code: '897792',
180
+ )
181
+ end.to raise_error(WorkOS::InvalidRequestError)
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end
187
+
188
+ describe 'verify_factor with invalid argument' do
189
+ context 'missing code argument' do
190
+ it 'returns argument error' do
191
+ expect do
192
+ described_class.verify_factor(
193
+ authentication_challenge_id: 'auth_challenge_01FZ4YVRBMXP5ZM0A7BP4AJ12J',
194
+ )
195
+ end.to raise_error(
196
+ ArgumentError,
197
+ "Incomplete arguments: 'authentication_challenge_id' and 'code' are required arguments",
198
+ )
199
+ end
200
+ end
201
+
202
+ context 'missing authentication_challenge_id argument' do
203
+ it 'returns and error' do
204
+ expect do
205
+ described_class.verify_factor(
206
+ code: '897792',
207
+ )
208
+ end.to raise_error(
209
+ ArgumentError,
210
+ "Incomplete arguments: 'authentication_challenge_id' and 'code' are required arguments",
211
+ )
212
+ end
213
+ end
214
+
215
+ context 'missing code and authentication_challenge_id arguments' do
216
+ it 'returns argument error' do
217
+ expect do
218
+ described_class.verify_factor
219
+ end.to raise_error(
220
+ ArgumentError,
221
+ "Incomplete arguments: 'authentication_challenge_id' and 'code' are required arguments",
222
+ )
223
+ end
224
+ end
225
+ end
226
+
227
+ describe 'tests returning and deleting a factor' do
228
+ context 'returns a factor' do
229
+ it 'uses get_factor to return factor' do
230
+ VCR.use_cassette 'mfa/get_factor_valid' do
231
+ factor = described_class.get_factor(
232
+ id: 'auth_factor_01FZ4WMXXA09XF6NK1XMKNWB3M',
233
+ )
234
+ expect(factor.id.instance_of?(String))
235
+ end
236
+ end
237
+ end
238
+
239
+ context 'invalid factor request' do
240
+ it 'uses get_factor and throws error if id is wrong' do
241
+ VCR.use_cassette 'mfa/get_factor_invalid' do
242
+ expect do
243
+ described_class.get_factor(
244
+ id: 'auth_factor_invalid',
245
+ )
246
+ end.to raise_error(WorkOS::APIError)
247
+ end
248
+ end
249
+ end
250
+
251
+ context 'deletes facotr' do
252
+ it 'uses delete_factor to delete factor' do
253
+ VCR.use_cassette 'mfa/delete_factor' do
254
+ response = described_class.delete_factor(
255
+ id: 'auth_factor_01FZ4WMXXA09XF6NK1XMKNWB3M',
256
+ )
257
+ expect(response).to be(true)
258
+ end
259
+ end
260
+ end
261
+ end
262
+ end
@@ -20,7 +20,6 @@ describe WorkOS::SSO do
20
20
  end
21
21
  it 'returns a valid URL' do
22
22
  authorization_url = described_class.authorization_url(**args)
23
-
24
23
  expect(URI.parse(authorization_url)).to be_a URI
25
24
  end
26
25
 
@@ -315,7 +314,6 @@ describe WorkOS::SSO do
315
314
  verified_email: true,
316
315
  },
317
316
  }
318
-
319
317
  expect(profile.to_json).to eq(expectation)
320
318
  end
321
319
  end
@@ -177,7 +177,7 @@ describe WorkOS::Webhooks do
177
177
  expect do
178
178
  described_class.construct_event(
179
179
  payload: @payload,
180
- sig_header: "t=9999, v1=#{@signature_hash}",
180
+ sig_header: "t=#{@timestamp.to_i - (200 * 1000)}, v1=#{@signature_hash}",
181
181
  secret: @secret,
182
182
  )
183
183
  end.to raise_error(
@@ -0,0 +1,82 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://api.workos.com/auth/factors/challenge
6
+ body:
7
+ encoding: UTF-8
8
+ string: '{"sms_template":null,"authentication_factor_id":"auth_factor_01FZ4WMXXA09XF6NK1XMKNWB3M"}'
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.2; arm64-darwin21; v2.1.1
18
+ Authorization:
19
+ - Bearer <API_KEY>
20
+ response:
21
+ status:
22
+ code: 201
23
+ message: Created
24
+ headers:
25
+ Date:
26
+ - Sun, 27 Mar 2022 05:15:26 GMT
27
+ Content-Type:
28
+ - application/json; charset=utf-8
29
+ Content-Length:
30
+ - '290'
31
+ Connection:
32
+ - keep-alive
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
+ Vary:
56
+ - Origin, Accept-Encoding
57
+ Access-Control-Allow-Credentials:
58
+ - 'true'
59
+ X-Request-Id:
60
+ - 6f320a1f-92d8-496c-9c30-69355787d21e
61
+ Etag:
62
+ - W/"122-QtSiaXex7UKEyydEC3oPpuHzGNw"
63
+ Via:
64
+ - 1.1 vegur
65
+ Cf-Cache-Status:
66
+ - DYNAMIC
67
+ Report-To:
68
+ - '{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v3?s=2kePZ4Q7kGQ1by5iXZxRevmSkQq4vTbW1vXTqFev99eLBrrfEHOLK2%2FE0ItWB2GAKoQvhPBk3rhS%2FKg0rtK3ZH4DGTf%2FEQKGFPT6BxtCqQE2L%2ByfEv1AgU152ZwIBPVYjQ%3D%3D"}],"group":"cf-nel","max_age":604800}'
69
+ Nel:
70
+ - '{"success_fraction":0,"report_to":"cf-nel","max_age":604800}'
71
+ Server:
72
+ - cloudflare
73
+ Cf-Ray:
74
+ - 6f25a5f0dddf088d-SEA
75
+ Alt-Svc:
76
+ - h3=":443"; ma=86400, h3-29=":443"; ma=86400
77
+ body:
78
+ encoding: UTF-8
79
+ string: '{"object":"authentication_challenge","id":"auth_challenge_01FZ4WSWV2SCEDX3GKY1NA9YTN","created_at":"2022-03-27T05:15:26.432Z","updated_at":"2022-03-27T05:15:26.432Z","expires_at":"2022-03-27T05:25:26.434Z","code":"541295","authentication_factor_id":"auth_factor_01FZ4WMXXA09XF6NK1XMKNWB3M"}'
80
+ http_version:
81
+ recorded_at: Sun, 27 Mar 2022 05:15:26 GMT
82
+ recorded_with: VCR 5.0.0
@@ -0,0 +1,82 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://api.workos.com/auth/factors/challenge
6
+ body:
7
+ encoding: UTF-8
8
+ string: '{"sms_template":"Your code is {{code}}","authentication_factor_id":"auth_factor_01FZ4TS14D1PHFNZ9GF6YD8M1F"}'
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.2; arm64-darwin21; v2.1.1
18
+ Authorization:
19
+ - Bearer <API_KEY>
20
+ response:
21
+ status:
22
+ code: 201
23
+ message: Created
24
+ headers:
25
+ Date:
26
+ - Sun, 27 Mar 2022 05:03:26 GMT
27
+ Content-Type:
28
+ - application/json; charset=utf-8
29
+ Content-Length:
30
+ - '274'
31
+ Connection:
32
+ - keep-alive
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
+ Vary:
56
+ - Origin, Accept-Encoding
57
+ Access-Control-Allow-Credentials:
58
+ - 'true'
59
+ X-Request-Id:
60
+ - b9c66c87-adf4-4a9a-854f-d838f966ea5c
61
+ Etag:
62
+ - W/"112-VpD9nscbxE6VOcsJlK2RHEzlq3s"
63
+ Via:
64
+ - 1.1 vegur
65
+ Cf-Cache-Status:
66
+ - DYNAMIC
67
+ Report-To:
68
+ - '{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v3?s=yNaSbl1oXtixmxb7lJOn7tDKzD0mN8jSFHJmTsZfD6YTlSGeMrdBgfi3LUFeDv1ldKcdNe5eZ%2BkRrVM986RUizGOhL2xzdl2AkJEdudIRaaJCMdWbjQDmGLbC4OPFajMIA%3D%3D"}],"group":"cf-nel","max_age":604800}'
69
+ Nel:
70
+ - '{"success_fraction":0,"report_to":"cf-nel","max_age":604800}'
71
+ Server:
72
+ - cloudflare
73
+ Cf-Ray:
74
+ - 6f25945b9ee639b4-SEA
75
+ Alt-Svc:
76
+ - h3=":443"; ma=86400, h3-29=":443"; ma=86400
77
+ body:
78
+ encoding: UTF-8
79
+ string: '{"object":"authentication_challenge","id":"auth_challenge_01FZ4W3XG1VD8ZD5TXSYQMFMSR","created_at":"2022-03-27T05:03:26.203Z","updated_at":"2022-03-27T05:03:26.203Z","expires_at":"2022-03-27T05:13:26.204Z","authentication_factor_id":"auth_factor_01FZ4TS14D1PHFNZ9GF6YD8M1F"}'
80
+ http_version:
81
+ recorded_at: Sun, 27 Mar 2022 05:03:26 GMT
82
+ recorded_with: VCR 5.0.0