workos 2.0.0 → 2.2.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 (35) 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 +54 -0
  6. data/lib/workos/client.rb +2 -0
  7. data/lib/workos/directory_sync.rb +1 -0
  8. data/lib/workos/errors.rb +3 -1
  9. data/lib/workos/factor.rb +58 -0
  10. data/lib/workos/mfa.rb +165 -0
  11. data/lib/workos/sso.rb +38 -15
  12. data/lib/workos/types/challenge_struct.rb +18 -0
  13. data/lib/workos/types/factor_struct.rb +19 -0
  14. data/lib/workos/types/verify_factor_struct.rb +15 -0
  15. data/lib/workos/types.rb +3 -0
  16. data/lib/workos/verify_factor.rb +39 -0
  17. data/lib/workos/version.rb +1 -1
  18. data/lib/workos/webhooks.rb +9 -7
  19. data/lib/workos.rb +5 -0
  20. data/spec/lib/workos/mfa_spec.rb +232 -0
  21. data/spec/lib/workos/sso_spec.rb +140 -4
  22. data/spec/lib/workos/webhooks_spec.rb +1 -1
  23. data/spec/support/fixtures/vcr_cassettes/mfa/challenge_factor_generic_valid.yml +82 -0
  24. data/spec/support/fixtures/vcr_cassettes/mfa/challenge_factor_sms_valid.yml +82 -0
  25. data/spec/support/fixtures/vcr_cassettes/mfa/challenge_factor_totp_valid.yml +82 -0
  26. data/spec/support/fixtures/vcr_cassettes/mfa/delete_factor.yml +80 -0
  27. data/spec/support/fixtures/vcr_cassettes/mfa/enroll_factor_generic_valid.yml +82 -0
  28. data/spec/support/fixtures/vcr_cassettes/mfa/enroll_factor_sms_valid.yml +82 -0
  29. data/spec/support/fixtures/vcr_cassettes/mfa/enroll_factor_totp_valid.yml +82 -0
  30. data/spec/support/fixtures/vcr_cassettes/mfa/get_factor_invalid.yml +82 -0
  31. data/spec/support/fixtures/vcr_cassettes/mfa/get_factor_valid.yml +82 -0
  32. data/spec/support/fixtures/vcr_cassettes/mfa/verify_factor_generic_expired.yml +84 -0
  33. data/spec/support/fixtures/vcr_cassettes/mfa/verify_factor_generic_invalid.yml +84 -0
  34. data/spec/support/fixtures/vcr_cassettes/mfa/verify_factor_generic_valid.yml +82 -0
  35. metadata +36 -3
@@ -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
@@ -44,6 +44,11 @@ module WorkOS
44
44
  autoload :DirectoryUser, 'workos/directory_user'
45
45
  autoload :Webhook, 'workos/webhook'
46
46
  autoload :Webhooks, 'workos/webhooks'
47
+ autoload :MFA, 'workos/mfa'
48
+ autoload :Factor, 'workos/factor'
49
+ autoload :Challenge, 'workos/challenge'
50
+ autoload :VerifyFactor, 'workos/verify_factor'
51
+
47
52
 
48
53
  # Errors
49
54
  autoload :APIError, 'workos/errors'
@@ -0,0 +1,232 @@
1
+ # frozen_string_literal: true
2
+ # typed: false
3
+
4
+ describe WorkOS::MFA do
5
+ it_behaves_like 'client'
6
+ describe 'enroll_factor valid requests' do
7
+ context 'enroll factor using valid generic argument' do
8
+ it 'returns a valid factor object' do
9
+ VCR.use_cassette 'mfa/enroll_factor_generic_valid' do
10
+ factor = described_class.enroll_factor(
11
+ type: 'generic_otp',
12
+ )
13
+ expect(factor.type == 'generic_otp')
14
+ end
15
+ end
16
+ end
17
+ context 'enroll factor using valid totp arguments' do
18
+ it 'returns a valid factor object' do
19
+ VCR.use_cassette 'mfa/enroll_factor_totp_valid' do
20
+ factor = described_class.enroll_factor(
21
+ type: 'totp',
22
+ totp_issuer: 'WorkOS',
23
+ totp_user: 'some_user',
24
+ )
25
+ expect(factor.totp.instance_of?(Hash))
26
+ end
27
+ end
28
+ end
29
+ context 'enroll factor using valid sms arguments' do
30
+ it 'returns a valid factor object' do
31
+ VCR.use_cassette 'mfa/enroll_factor_sms_valid' do
32
+ factor = described_class.enroll_factor(
33
+ type: 'sms',
34
+ phone_number: '55555555555',
35
+ )
36
+ expect(factor.sms.instance_of?(Hash))
37
+ end
38
+ end
39
+ end
40
+ end
41
+ describe 'enroll_factor invalid responses' do
42
+ context 'enroll factor throws error if type is not sms or totp' do
43
+ it 'returns an error' do
44
+ expect do
45
+ described_class.enroll_factor(
46
+ type: 'invalid',
47
+ phone_number: '+15005550006',
48
+ )
49
+ end.to raise_error(
50
+ ArgumentError,
51
+ "Type argument must be either 'sms' or 'totp'",
52
+ )
53
+ end
54
+ end
55
+ context 'enroll factor throws error if type is not sms or totp' do
56
+ it 'returns an error' do
57
+ expect do
58
+ described_class.enroll_factor(
59
+ type: 'totp',
60
+ totp_issuer: 'WorkOS',
61
+ )
62
+ end.to raise_error(
63
+ ArgumentError,
64
+ 'Incomplete arguments. Need to specify both totp_issuer and totp_user when type is totp',
65
+ )
66
+ end
67
+ end
68
+ context 'enroll factor throws error if type sms and phone number is nil' do
69
+ it 'returns an error' do
70
+ expect do
71
+ described_class.enroll_factor(
72
+ type: 'sms',
73
+ )
74
+ end.to raise_error(
75
+ ArgumentError,
76
+ 'Incomplete arguments. Need to specify phone_number when type is sms',
77
+ )
78
+ end
79
+ end
80
+ end
81
+ describe 'challenge factor with valid request arguments' do
82
+ context 'challenge with totp' do
83
+ it 'returns challenge factor object for totp' do
84
+ VCR.use_cassette 'mfa/challenge_factor_totp_valid' do
85
+ challenge_factor = described_class.challenge_factor(
86
+ authentication_factor_id: 'auth_factor_01FZ4TS0MWPZR7GATS7KCXANQZ',
87
+ )
88
+ expect(challenge_factor.authentication_factor_id.class.instance_of?(String))
89
+ end
90
+ end
91
+ end
92
+ context 'challenge with sms' do
93
+ it 'returns a challenge factor object for sms' do
94
+ VCR.use_cassette 'mfa/challenge_factor_sms_valid' do
95
+ challenge_factor = described_class.challenge_factor(
96
+ authentication_factor_id: 'auth_factor_01FZ4TS14D1PHFNZ9GF6YD8M1F',
97
+ sms_template: 'Your code is {{code}}',
98
+ )
99
+ expect(challenge_factor.authentication_factor_id.instance_of?(String))
100
+ end
101
+ end
102
+ end
103
+ context 'challenge with generic' do
104
+ it 'returns a valid challenge factor object for generic otp' do
105
+ VCR.use_cassette 'mfa/challenge_factor_generic_valid' do
106
+ challenge_factor = described_class.challenge_factor(
107
+ authentication_factor_id: 'auth_factor_01FZ4WMXXA09XF6NK1XMKNWB3M',
108
+ )
109
+ expect(challenge_factor.code.instance_of?(String))
110
+ end
111
+ end
112
+ end
113
+ end
114
+ describe 'challenge factor with invalid arguments' do
115
+ context 'challenge with totp mssing authentication_factor_id' do
116
+ it 'returns argument error' do
117
+ expect do
118
+ described_class.challenge_factor
119
+ end.to raise_error(
120
+ ArgumentError,
121
+ "Incomplete arguments: 'authentication_factor_id' is a required argument",
122
+ )
123
+ end
124
+ end
125
+ end
126
+ describe 'challenge factor with valid requests' do
127
+ context 'verify generic otp' do
128
+ it 'returns a true boolean if the challenge has not been verifed yet' do
129
+ VCR.use_cassette 'mfa/verify_factor_generic_valid' do
130
+ verify_factor = described_class.verify_factor(
131
+ authentication_challenge_id: 'auth_challenge_01FZ4YVRBMXP5ZM0A7BP4AJ12J',
132
+ code: '897792',
133
+ )
134
+ expect(verify_factor.valid == 'true')
135
+ end
136
+ end
137
+ end
138
+ context 'verify generic otp' do
139
+ it 'returns error that the challenge has already been verfied' do
140
+ VCR.use_cassette 'mfa/verify_factor_generic_invalid' do
141
+ expect do
142
+ described_class.verify_factor(
143
+ authentication_challenge_id: 'auth_challenge_01FZ4YVRBMXP5ZM0A7BP4AJ12J',
144
+ code: '897792',
145
+ )
146
+ end.to raise_error(WorkOS::InvalidRequestError)
147
+ end
148
+ end
149
+ context 'verify generic otp' do
150
+ it 'returns error that the challenge has expired' do
151
+ VCR.use_cassette 'mfa/verify_factor_generic_expired' do
152
+ expect do
153
+ described_class.verify_factor(
154
+ authentication_challenge_id: 'auth_challenge_01FZ4YVRBMXP5ZM0A7BP4AJ12J',
155
+ code: '897792',
156
+ )
157
+ end.to raise_error(WorkOS::InvalidRequestError)
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
163
+ describe 'verify_factor with invalid argument' do
164
+ context 'missing code argument' do
165
+ it 'returns argument error' do
166
+ expect do
167
+ described_class.verify_factor(
168
+ authentication_challenge_id: 'auth_challenge_01FZ4YVRBMXP5ZM0A7BP4AJ12J',
169
+ )
170
+ end.to raise_error(
171
+ ArgumentError,
172
+ "Incomplete arguments: 'authentication_challenge_id' and 'code' are required arguments",
173
+ )
174
+ end
175
+ end
176
+ context 'missing authentication_challenge_id argument' do
177
+ it '' do
178
+ expect do
179
+ described_class.verify_factor(
180
+ code: '897792',
181
+ )
182
+ end.to raise_error(
183
+ ArgumentError,
184
+ "Incomplete arguments: 'authentication_challenge_id' and 'code' are required arguments",
185
+ )
186
+ end
187
+ end
188
+ context 'missing code and authentication_challenge_id arguments' do
189
+ it 'returns argument error' do
190
+ expect do
191
+ described_class.verify_factor
192
+ end.to raise_error(
193
+ ArgumentError,
194
+ "Incomplete arguments: 'authentication_challenge_id' and 'code' are required arguments",
195
+ )
196
+ end
197
+ end
198
+ end
199
+ describe 'tests returning and deleting a factor' do
200
+ context 'returns a factor' do
201
+ it 'uses get_factor to return factor' do
202
+ VCR.use_cassette 'mfa/get_factor_valid' do
203
+ factor = described_class.get_factor(
204
+ id: 'auth_factor_01FZ4WMXXA09XF6NK1XMKNWB3M',
205
+ )
206
+ expect(factor.id.instance_of?(String))
207
+ end
208
+ end
209
+ end
210
+ context 'invalid factor request' do
211
+ it 'uses get_factor and throws error if id is wrong' do
212
+ VCR.use_cassette 'mfa/get_factor_invalid' do
213
+ expect do
214
+ described_class.get_factor(
215
+ id: 'auth_factor_invalid',
216
+ )
217
+ end.to raise_error(WorkOS::APIError)
218
+ end
219
+ end
220
+ end
221
+ context 'deletes facotr' do
222
+ it 'uses delete_factor to delete factor' do
223
+ VCR.use_cassette 'mfa/delete_factor' do
224
+ response = described_class.delete_factor(
225
+ id: 'auth_factor_01FZ4WMXXA09XF6NK1XMKNWB3M',
226
+ )
227
+ expect(response).to be(true)
228
+ end
229
+ end
230
+ end
231
+ end
232
+ 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
 
@@ -109,7 +108,145 @@ describe WorkOS::SSO do
109
108
  end
110
109
  end
111
110
 
112
- context 'with neither connection, domain, or provider' do
111
+ context 'with a domain' do
112
+ let(:args) do
113
+ {
114
+ domain: 'foo.com',
115
+ client_id: 'workos-proj-123',
116
+ redirect_uri: 'foo.com/auth/callback',
117
+ state: {
118
+ next_page: '/dashboard/edit',
119
+ }.to_s,
120
+ }
121
+ end
122
+ it 'returns a valid URL' do
123
+ authorization_url = described_class.authorization_url(**args)
124
+
125
+ expect(URI.parse(authorization_url)).to be_a URI
126
+ end
127
+
128
+ it 'returns the expected hostname' do
129
+ authorization_url = described_class.authorization_url(**args)
130
+
131
+ expect(URI.parse(authorization_url).host).to eq(WorkOS::API_HOSTNAME)
132
+ end
133
+
134
+ it 'returns the expected query string' do
135
+ authorization_url = described_class.authorization_url(**args)
136
+
137
+ expect(URI.parse(authorization_url).query).to eq(
138
+ 'client_id=workos-proj-123&redirect_uri=foo.com%2Fauth%2Fcallback' \
139
+ '&response_type=code&state=%7B%3Anext_page%3D%3E%22%2Fdashboard%2F' \
140
+ 'edit%22%7D&domain=foo.com',
141
+ )
142
+ end
143
+ end
144
+
145
+ context 'with a domain_hint' do
146
+ let(:args) do
147
+ {
148
+ connection: 'connection_123',
149
+ domain_hint: 'foo.com',
150
+ client_id: 'workos-proj-123',
151
+ redirect_uri: 'foo.com/auth/callback',
152
+ state: {
153
+ next_page: '/dashboard/edit',
154
+ }.to_s,
155
+ }
156
+ end
157
+ it 'returns a valid URL' do
158
+ authorization_url = described_class.authorization_url(**args)
159
+
160
+ expect(URI.parse(authorization_url)).to be_a URI
161
+ end
162
+
163
+ it 'returns the expected hostname' do
164
+ authorization_url = described_class.authorization_url(**args)
165
+
166
+ expect(URI.parse(authorization_url).host).to eq(WorkOS::API_HOSTNAME)
167
+ end
168
+
169
+ it 'returns the expected query string' do
170
+ authorization_url = described_class.authorization_url(**args)
171
+
172
+ expect(URI.parse(authorization_url).query).to eq(
173
+ 'client_id=workos-proj-123&redirect_uri=foo.com%2Fauth%2Fcallback' \
174
+ '&response_type=code&state=%7B%3Anext_page%3D%3E%22%2Fdashboard%2' \
175
+ 'Fedit%22%7D&domain_hint=foo.com&connection=connection_123',
176
+ )
177
+ end
178
+ end
179
+
180
+ context 'with a login_hint' do
181
+ let(:args) do
182
+ {
183
+ connection: 'connection_123',
184
+ login_hint: 'foo@workos.com',
185
+ client_id: 'workos-proj-123',
186
+ redirect_uri: 'foo.com/auth/callback',
187
+ state: {
188
+ next_page: '/dashboard/edit',
189
+ }.to_s,
190
+ }
191
+ end
192
+ it 'returns a valid URL' do
193
+ authorization_url = described_class.authorization_url(**args)
194
+
195
+ expect(URI.parse(authorization_url)).to be_a URI
196
+ end
197
+
198
+ it 'returns the expected hostname' do
199
+ authorization_url = described_class.authorization_url(**args)
200
+
201
+ expect(URI.parse(authorization_url).host).to eq(WorkOS::API_HOSTNAME)
202
+ end
203
+
204
+ it 'returns the expected query string' do
205
+ authorization_url = described_class.authorization_url(**args)
206
+
207
+ expect(URI.parse(authorization_url).query).to eq(
208
+ 'client_id=workos-proj-123&redirect_uri=foo.com%2Fauth%2Fcallback' \
209
+ '&response_type=code&state=%7B%3Anext_page%3D%3E%22%2Fdashboard%2' \
210
+ 'Fedit%22%7D&login_hint=foo%40workos.com&connection=connection_123',
211
+ )
212
+ end
213
+ end
214
+
215
+ context 'with an organization' do
216
+ let(:args) do
217
+ {
218
+ organization: 'org_123',
219
+ client_id: 'workos-proj-123',
220
+ redirect_uri: 'foo.com/auth/callback',
221
+ state: {
222
+ next_page: '/dashboard/edit',
223
+ }.to_s,
224
+ }
225
+ end
226
+ it 'returns a valid URL' do
227
+ authorization_url = described_class.authorization_url(**args)
228
+
229
+ expect(URI.parse(authorization_url)).to be_a URI
230
+ end
231
+
232
+ it 'returns the expected hostname' do
233
+ authorization_url = described_class.authorization_url(**args)
234
+
235
+ expect(URI.parse(authorization_url).host).to eq(WorkOS::API_HOSTNAME)
236
+ end
237
+
238
+ it 'returns the expected query string' do
239
+ authorization_url = described_class.authorization_url(**args)
240
+
241
+ expect(URI.parse(authorization_url).query).to eq(
242
+ 'client_id=workos-proj-123&redirect_uri=foo.com%2Fauth%2Fcallback' \
243
+ '&response_type=code&state=%7B%3Anext_page%3D%3E%22%2Fdashboard%2F' \
244
+ 'edit%22%7D&organization=org_123',
245
+ )
246
+ end
247
+ end
248
+
249
+ context 'with neither connection, domain, provider, or organization' do
113
250
  let(:args) do
114
251
  {
115
252
  client_id: 'workos-proj-123',
@@ -124,7 +261,7 @@ describe WorkOS::SSO do
124
261
  described_class.authorization_url(**args)
125
262
  end.to raise_error(
126
263
  ArgumentError,
127
- 'Either connection, domain, or provider is required.',
264
+ 'Either connection, domain, provider, or organization is required.',
128
265
  )
129
266
  end
130
267
  end
@@ -177,7 +314,6 @@ describe WorkOS::SSO do
177
314
  verified_email: true,
178
315
  },
179
316
  }
180
-
181
317
  expect(profile.to_json).to eq(expectation)
182
318
  end
183
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