workosv2 2.15.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 (242) hide show
  1. checksums.yaml +7 -0
  2. data/.github/CODEOWNERS +5 -0
  3. data/.github/pull_request_template.md +11 -0
  4. data/.github/renovate.json +5 -0
  5. data/.gitignore +49 -0
  6. data/.rspec +1 -0
  7. data/.rubocop.yml +24 -0
  8. data/.ruby-version +1 -0
  9. data/.semaphore/rubygems.yml +24 -0
  10. data/.semaphore/semaphore.yml +51 -0
  11. data/Gemfile +5 -0
  12. data/Gemfile.lock +126 -0
  13. data/Gemfile.lock.old +127 -0
  14. data/LICENSE +21 -0
  15. data/README.md +53 -0
  16. data/bin/build +3 -0
  17. data/bin/console +3 -0
  18. data/bin/docs +5 -0
  19. data/bin/publish +3 -0
  20. data/bin/tapioca +29 -0
  21. data/codecov.yml +12 -0
  22. data/docs/WorkOS/APIError.html +160 -0
  23. data/docs/WorkOS/AuditLog.html +235 -0
  24. data/docs/WorkOS/AuditTrail.html +235 -0
  25. data/docs/WorkOS/AuthenticationError.html +160 -0
  26. data/docs/WorkOS/Base.html +287 -0
  27. data/docs/WorkOS/Client.html +504 -0
  28. data/docs/WorkOS/InvalidRequestError.html +160 -0
  29. data/docs/WorkOS/Profile.html +788 -0
  30. data/docs/WorkOS/RequestError.html +135 -0
  31. data/docs/WorkOS/SSO.html +691 -0
  32. data/docs/WorkOS/Types/ProfileStruct.html +135 -0
  33. data/docs/WorkOS/Types/Provider.html +135 -0
  34. data/docs/WorkOS/Types.html +128 -0
  35. data/docs/WorkOS/WorkOSError.html +447 -0
  36. data/docs/WorkOS.html +324 -0
  37. data/docs/class_list.html +51 -0
  38. data/docs/css/common.css +1 -0
  39. data/docs/css/full_list.css +58 -0
  40. data/docs/css/style.css +496 -0
  41. data/docs/file.README.html +252 -0
  42. data/docs/file_list.html +56 -0
  43. data/docs/frames.html +17 -0
  44. data/docs/index.html +250 -0
  45. data/docs/js/app.js +314 -0
  46. data/docs/js/full_list.js +216 -0
  47. data/docs/js/jquery.js +4 -0
  48. data/docs/method_list.html +267 -0
  49. data/docs/top-level-namespace.html +110 -0
  50. data/lib/workosv2/audit_log_export.rb +55 -0
  51. data/lib/workosv2/audit_logs.rb +114 -0
  52. data/lib/workosv2/audit_trail.rb +111 -0
  53. data/lib/workosv2/challenge.rb +55 -0
  54. data/lib/workosv2/client.rb +186 -0
  55. data/lib/workosv2/configuration.rb +17 -0
  56. data/lib/workosv2/connection.rb +66 -0
  57. data/lib/workosv2/deprecated_hash_wrapper.rb +76 -0
  58. data/lib/workosv2/directory.rb +65 -0
  59. data/lib/workosv2/directory_group.rb +68 -0
  60. data/lib/workosv2/directory_sync.rb +218 -0
  61. data/lib/workosv2/directory_user.rb +97 -0
  62. data/lib/workosv2/errors.rb +81 -0
  63. data/lib/workosv2/event.rb +51 -0
  64. data/lib/workosv2/events.rb +52 -0
  65. data/lib/workosv2/factor.rb +54 -0
  66. data/lib/workosv2/hash_provider.rb +19 -0
  67. data/lib/workosv2/mfa.rb +178 -0
  68. data/lib/workosv2/organization.rb +57 -0
  69. data/lib/workosv2/organizations.rb +188 -0
  70. data/lib/workosv2/passwordless.rb +85 -0
  71. data/lib/workosv2/portal.rb +66 -0
  72. data/lib/workosv2/profile.rb +76 -0
  73. data/lib/workosv2/profile_and_token.rb +29 -0
  74. data/lib/workosv2/sso.rb +297 -0
  75. data/lib/workosv2/types/audit_log_export_struct.rb +17 -0
  76. data/lib/workosv2/types/challenge_struct.rb +18 -0
  77. data/lib/workosv2/types/connection_struct.rb +20 -0
  78. data/lib/workosv2/types/directory_group_struct.rb +19 -0
  79. data/lib/workosv2/types/directory_struct.rb +19 -0
  80. data/lib/workosv2/types/directory_user_struct.rb +26 -0
  81. data/lib/workosv2/types/event_struct.rb +15 -0
  82. data/lib/workosv2/types/factor_struct.rb +18 -0
  83. data/lib/workosv2/types/intent_enum.rb +17 -0
  84. data/lib/workosv2/types/list_struct.rb +13 -0
  85. data/lib/workosv2/types/organization_struct.rb +17 -0
  86. data/lib/workosv2/types/passwordless_session_struct.rb +17 -0
  87. data/lib/workosv2/types/profile_struct.rb +21 -0
  88. data/lib/workosv2/types/provider_enum.rb +15 -0
  89. data/lib/workosv2/types/verify_challenge_struct.rb +13 -0
  90. data/lib/workosv2/types/webhook_struct.rb +15 -0
  91. data/lib/workosv2/types.rb +25 -0
  92. data/lib/workosv2/verify_challenge.rb +39 -0
  93. data/lib/workosv2/version.rb +6 -0
  94. data/lib/workosv2/webhook.rb +51 -0
  95. data/lib/workosv2/webhooks.rb +217 -0
  96. data/lib/workosv2.rb +79 -0
  97. data/sorbet/config +2 -0
  98. data/sorbet/rbi/gems/addressable@2.8.0.rbi +290 -0
  99. data/sorbet/rbi/gems/ast@2.4.2.rbi +54 -0
  100. data/sorbet/rbi/gems/codecov@0.2.12.rbi +55 -0
  101. data/sorbet/rbi/gems/coderay@1.1.3.rbi +8 -0
  102. data/sorbet/rbi/gems/crack@0.4.5.rbi +57 -0
  103. data/sorbet/rbi/gems/diff-lcs@1.4.4.rbi +185 -0
  104. data/sorbet/rbi/gems/docile@1.3.5.rbi +54 -0
  105. data/sorbet/rbi/gems/hashdiff@1.0.1.rbi +82 -0
  106. data/sorbet/rbi/gems/json@2.5.1.rbi +109 -0
  107. data/sorbet/rbi/gems/method_source@1.0.0.rbi +8 -0
  108. data/sorbet/rbi/gems/parallel@1.20.1.rbi +113 -0
  109. data/sorbet/rbi/gems/parser@3.0.1.0.rbi +1187 -0
  110. data/sorbet/rbi/gems/pry@0.14.2.rbi +8 -0
  111. data/sorbet/rbi/gems/public_suffix@4.0.6.rbi +146 -0
  112. data/sorbet/rbi/gems/rainbow@3.0.0.rbi +153 -0
  113. data/sorbet/rbi/gems/rake@13.0.3.rbi +807 -0
  114. data/sorbet/rbi/gems/rbi@0.0.16.rbi +2118 -0
  115. data/sorbet/rbi/gems/regexp_parser@2.1.1.rbi +1117 -0
  116. data/sorbet/rbi/gems/rexml@3.2.5.rbi +709 -0
  117. data/sorbet/rbi/gems/rspec-core@3.9.3.rbi +2467 -0
  118. data/sorbet/rbi/gems/rspec-expectations@3.9.4.rbi +1569 -0
  119. data/sorbet/rbi/gems/rspec-mocks@3.9.1.rbi +1493 -0
  120. data/sorbet/rbi/gems/rspec-support@3.9.4.rbi +511 -0
  121. data/sorbet/rbi/gems/rspec@3.9.0.rbi +38 -0
  122. data/sorbet/rbi/gems/rubocop-ast@1.4.1.rbi +1881 -0
  123. data/sorbet/rbi/gems/rubocop@0.93.1.rbi +11497 -0
  124. data/sorbet/rbi/gems/ruby-progressbar@1.11.0.rbi +405 -0
  125. data/sorbet/rbi/gems/simplecov-html@0.12.3.rbi +89 -0
  126. data/sorbet/rbi/gems/simplecov@0.21.2.rbi +577 -0
  127. data/sorbet/rbi/gems/simplecov_json_formatter@0.1.2.rbi +8 -0
  128. data/sorbet/rbi/gems/spoom@1.1.15.rbi +1549 -0
  129. data/sorbet/rbi/gems/tapioca@0.7.3.rbi +1718 -0
  130. data/sorbet/rbi/gems/thor@1.2.1.rbi +844 -0
  131. data/sorbet/rbi/gems/unicode-display_width@1.7.0.rbi +22 -0
  132. data/sorbet/rbi/gems/unparser@0.6.2.rbi +8 -0
  133. data/sorbet/rbi/gems/vcr@5.0.0.rbi +699 -0
  134. data/sorbet/rbi/gems/webmock@3.12.2.rbi +662 -0
  135. data/sorbet/rbi/gems/yard-sorbet@0.8.0.rbi +268 -0
  136. data/sorbet/rbi/gems/yard@0.9.26.rbi +4048 -0
  137. data/sorbet/tapioca/config.yml +13 -0
  138. data/sorbet/tapioca/require.rb +4 -0
  139. data/spec/lib/workos/audit_logs_spec.rb +151 -0
  140. data/spec/lib/workos/audit_trail_spec.rb +146 -0
  141. data/spec/lib/workos/configuration_spec.rb +61 -0
  142. data/spec/lib/workos/directory_sync_spec.rb +492 -0
  143. data/spec/lib/workos/directory_user_spec.rb +36 -0
  144. data/spec/lib/workos/event_spec.rb +88 -0
  145. data/spec/lib/workos/mfa_spec.rb +281 -0
  146. data/spec/lib/workos/organizations_spec.rb +257 -0
  147. data/spec/lib/workos/passwordless_spec.rb +77 -0
  148. data/spec/lib/workos/portal_spec.rb +87 -0
  149. data/spec/lib/workos/sso_spec.rb +650 -0
  150. data/spec/lib/workos/webhooks_spec.rb +236 -0
  151. data/spec/spec_helper.rb +56 -0
  152. data/spec/support/fixtures/vcr_cassettes/audit_logs/create_event.yml +59 -0
  153. data/spec/support/fixtures/vcr_cassettes/audit_logs/create_event_custom_idempotency_key.yml +60 -0
  154. data/spec/support/fixtures/vcr_cassettes/audit_logs/create_event_invalid.yml +59 -0
  155. data/spec/support/fixtures/vcr_cassettes/audit_logs/create_export.yml +76 -0
  156. data/spec/support/fixtures/vcr_cassettes/audit_logs/create_export_with_filters.yml +77 -0
  157. data/spec/support/fixtures/vcr_cassettes/audit_logs/get_export.yml +73 -0
  158. data/spec/support/fixtures/vcr_cassettes/audit_trail/create_event.yml +65 -0
  159. data/spec/support/fixtures/vcr_cassettes/audit_trail/create_event_custom_idempotency_key.yml +67 -0
  160. data/spec/support/fixtures/vcr_cassettes/audit_trail/create_event_invalid.yml +68 -0
  161. data/spec/support/fixtures/vcr_cassettes/audit_trail/create_events_duplicate_idempotency_key_and_payload.yml +131 -0
  162. data/spec/support/fixtures/vcr_cassettes/audit_trail/create_events_duplicate_idempotency_key_different_payload.yml +134 -0
  163. data/spec/support/fixtures/vcr_cassettes/audit_trail/get_events.yml +61 -0
  164. data/spec/support/fixtures/vcr_cassettes/base/execute_request_unauthenticated.yml +66 -0
  165. data/spec/support/fixtures/vcr_cassettes/directory_sync/delete_directory.yml +72 -0
  166. data/spec/support/fixtures/vcr_cassettes/directory_sync/get_directory_with_invalid_id.yml +83 -0
  167. data/spec/support/fixtures/vcr_cassettes/directory_sync/get_directory_with_valid_id.yml +84 -0
  168. data/spec/support/fixtures/vcr_cassettes/directory_sync/get_group.yml +80 -0
  169. data/spec/support/fixtures/vcr_cassettes/directory_sync/get_group_with_invalid_id.yml +62 -0
  170. data/spec/support/fixtures/vcr_cassettes/directory_sync/get_user.yml +83 -0
  171. data/spec/support/fixtures/vcr_cassettes/directory_sync/get_user_with_invalid_id.yml +62 -0
  172. data/spec/support/fixtures/vcr_cassettes/directory_sync/list_directories/with_after.yml +87 -0
  173. data/spec/support/fixtures/vcr_cassettes/directory_sync/list_directories/with_before.yml +89 -0
  174. data/spec/support/fixtures/vcr_cassettes/directory_sync/list_directories/with_domain.yml +84 -0
  175. data/spec/support/fixtures/vcr_cassettes/directory_sync/list_directories/with_limit.yml +85 -0
  176. data/spec/support/fixtures/vcr_cassettes/directory_sync/list_directories/with_no_options.yml +93 -0
  177. data/spec/support/fixtures/vcr_cassettes/directory_sync/list_directories/with_search.yml +85 -0
  178. data/spec/support/fixtures/vcr_cassettes/directory_sync/list_groups/with_after.yml +90 -0
  179. data/spec/support/fixtures/vcr_cassettes/directory_sync/list_groups/with_before.yml +90 -0
  180. data/spec/support/fixtures/vcr_cassettes/directory_sync/list_groups/with_directory.yml +90 -0
  181. data/spec/support/fixtures/vcr_cassettes/directory_sync/list_groups/with_limit.yml +84 -0
  182. data/spec/support/fixtures/vcr_cassettes/directory_sync/list_groups/with_no_options.yml +84 -0
  183. data/spec/support/fixtures/vcr_cassettes/directory_sync/list_groups/with_user.yml +82 -0
  184. data/spec/support/fixtures/vcr_cassettes/directory_sync/list_users/with_after.yml +186 -0
  185. data/spec/support/fixtures/vcr_cassettes/directory_sync/list_users/with_before.yml +88 -0
  186. data/spec/support/fixtures/vcr_cassettes/directory_sync/list_users/with_directory.yml +194 -0
  187. data/spec/support/fixtures/vcr_cassettes/directory_sync/list_users/with_group.yml +186 -0
  188. data/spec/support/fixtures/vcr_cassettes/directory_sync/list_users/with_limit.yml +189 -0
  189. data/spec/support/fixtures/vcr_cassettes/directory_sync/list_users/with_no_options.yml +74 -0
  190. data/spec/support/fixtures/vcr_cassettes/events/list_events_with_after.yml +80 -0
  191. data/spec/support/fixtures/vcr_cassettes/events/list_events_with_event.yml +80 -0
  192. data/spec/support/fixtures/vcr_cassettes/events/list_events_with_no_options.yml +80 -0
  193. data/spec/support/fixtures/vcr_cassettes/events/list_events_with_range.yml +80 -0
  194. data/spec/support/fixtures/vcr_cassettes/mfa/challenge_factor_generic_valid.yml +82 -0
  195. data/spec/support/fixtures/vcr_cassettes/mfa/challenge_factor_sms_valid.yml +82 -0
  196. data/spec/support/fixtures/vcr_cassettes/mfa/challenge_factor_totp_valid.yml +82 -0
  197. data/spec/support/fixtures/vcr_cassettes/mfa/delete_factor.yml +80 -0
  198. data/spec/support/fixtures/vcr_cassettes/mfa/enroll_factor_generic_valid.yml +82 -0
  199. data/spec/support/fixtures/vcr_cassettes/mfa/enroll_factor_sms_valid.yml +82 -0
  200. data/spec/support/fixtures/vcr_cassettes/mfa/enroll_factor_totp_valid.yml +82 -0
  201. data/spec/support/fixtures/vcr_cassettes/mfa/get_factor_invalid.yml +82 -0
  202. data/spec/support/fixtures/vcr_cassettes/mfa/get_factor_valid.yml +82 -0
  203. data/spec/support/fixtures/vcr_cassettes/mfa/verify_challenge_generic_expired.yml +84 -0
  204. data/spec/support/fixtures/vcr_cassettes/mfa/verify_challenge_generic_invalid.yml +84 -0
  205. data/spec/support/fixtures/vcr_cassettes/mfa/verify_challenge_generic_valid.yml +82 -0
  206. data/spec/support/fixtures/vcr_cassettes/mfa/verify_challenge_generic_valid_is_false.yml +82 -0
  207. data/spec/support/fixtures/vcr_cassettes/organization/create.yml +84 -0
  208. data/spec/support/fixtures/vcr_cassettes/organization/create_invalid.yml +72 -0
  209. data/spec/support/fixtures/vcr_cassettes/organization/create_with_duplicate_idempotency_key_and_different_payload.yml +155 -0
  210. data/spec/support/fixtures/vcr_cassettes/organization/create_with_duplicate_idempotency_key_and_payload.yml +154 -0
  211. data/spec/support/fixtures/vcr_cassettes/organization/create_with_idempotency_key.yml +79 -0
  212. data/spec/support/fixtures/vcr_cassettes/organization/delete.yml +72 -0
  213. data/spec/support/fixtures/vcr_cassettes/organization/delete_invalid.yml +72 -0
  214. data/spec/support/fixtures/vcr_cassettes/organization/get.yml +84 -0
  215. data/spec/support/fixtures/vcr_cassettes/organization/get_invalid.yml +72 -0
  216. data/spec/support/fixtures/vcr_cassettes/organization/list.yml +87 -0
  217. data/spec/support/fixtures/vcr_cassettes/organization/update.yml +84 -0
  218. data/spec/support/fixtures/vcr_cassettes/passwordless/create_session.yml +72 -0
  219. data/spec/support/fixtures/vcr_cassettes/passwordless/create_session_invalid.yml +73 -0
  220. data/spec/support/fixtures/vcr_cassettes/passwordless/send_session.yml +72 -0
  221. data/spec/support/fixtures/vcr_cassettes/passwordless/send_session_invalid.yml +73 -0
  222. data/spec/support/fixtures/vcr_cassettes/portal/generate_link_audit_logs.yml +72 -0
  223. data/spec/support/fixtures/vcr_cassettes/portal/generate_link_dsync.yml +72 -0
  224. data/spec/support/fixtures/vcr_cassettes/portal/generate_link_invalid.yml +72 -0
  225. data/spec/support/fixtures/vcr_cassettes/portal/generate_link_sso.yml +72 -0
  226. data/spec/support/fixtures/vcr_cassettes/sso/delete_connection_with_invalid_id.yml +72 -0
  227. data/spec/support/fixtures/vcr_cassettes/sso/delete_connection_with_valid_id.yml +70 -0
  228. data/spec/support/fixtures/vcr_cassettes/sso/get_connection_with_invalid_id.yml +72 -0
  229. data/spec/support/fixtures/vcr_cassettes/sso/get_connection_with_valid_id.yml +86 -0
  230. data/spec/support/fixtures/vcr_cassettes/sso/list_connections/with_after.yml +83 -0
  231. data/spec/support/fixtures/vcr_cassettes/sso/list_connections/with_before.yml +86 -0
  232. data/spec/support/fixtures/vcr_cassettes/sso/list_connections/with_connection_type.yml +90 -0
  233. data/spec/support/fixtures/vcr_cassettes/sso/list_connections/with_domain.yml +86 -0
  234. data/spec/support/fixtures/vcr_cassettes/sso/list_connections/with_limit.yml +83 -0
  235. data/spec/support/fixtures/vcr_cassettes/sso/list_connections/with_no_options.yml +89 -0
  236. data/spec/support/fixtures/vcr_cassettes/sso/list_connections/with_organization_id.yml +86 -0
  237. data/spec/support/fixtures/vcr_cassettes/sso/profile.yml +74 -0
  238. data/spec/support/profile.txt +1 -0
  239. data/spec/support/shared_examples/client_spec.rb +30 -0
  240. data/spec/support/webhook_payload.txt +1 -0
  241. data/workosv2.gemspec +38 -0
  242. metadata +531 -0
@@ -0,0 +1,281 @@
1
+ # frozen_string_literal: true
2
+ # typed: false
3
+
4
+ describe WorkOSV2::MFA do
5
+ it_behaves_like 'client'
6
+
7
+ describe '.enroll_factor' do
8
+ context 'with 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 'with 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: 'WorkOSV2',
25
+ totp_user: 'some_user',
26
+ )
27
+ expect(factor.totp.instance_of?(Hash))
28
+ end
29
+ end
30
+ end
31
+
32
+ context 'with 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
+
44
+ context 'when type is not sms or totp' do
45
+ it 'returns an error' do
46
+ expect do
47
+ described_class.enroll_factor(
48
+ type: 'invalid',
49
+ phone_number: '+15005550006',
50
+ )
51
+ end.to raise_error(
52
+ ArgumentError,
53
+ "Type argument must be either 'sms' or 'totp'",
54
+ )
55
+ end
56
+ end
57
+
58
+ context 'when type is totp but missing arguments' do
59
+ it 'returns an error' do
60
+ expect do
61
+ described_class.enroll_factor(
62
+ type: 'totp',
63
+ totp_issuer: 'WorkOSV2',
64
+ )
65
+ end.to raise_error(
66
+ ArgumentError,
67
+ 'Incomplete arguments. Need to specify both totp_issuer and totp_user when type is totp',
68
+ )
69
+ end
70
+ end
71
+ context 'when type is sms and phone number is nil' do
72
+ it 'returns an error' do
73
+ expect do
74
+ described_class.enroll_factor(
75
+ type: 'sms',
76
+ )
77
+ end.to raise_error(
78
+ ArgumentError,
79
+ 'Incomplete arguments. Need to specify phone_number when type is sms',
80
+ )
81
+ end
82
+ end
83
+ end
84
+
85
+ describe '.challenge_factor' do
86
+ context 'challenge with totp' do
87
+ it 'returns challenge factor object for totp' do
88
+ VCR.use_cassette 'mfa/challenge_factor_totp_valid' do
89
+ challenge_factor = described_class.challenge_factor(
90
+ authentication_factor_id: 'auth_factor_01FZ4TS0MWPZR7GATS7KCXANQZ',
91
+ )
92
+ expect(challenge_factor.authentication_factor_id.class.instance_of?(String))
93
+ end
94
+ end
95
+ end
96
+
97
+ context 'challenge with sms' do
98
+ it 'returns a challenge factor object for sms' do
99
+ VCR.use_cassette 'mfa/challenge_factor_sms_valid' do
100
+ challenge_factor = described_class.challenge_factor(
101
+ authentication_factor_id: 'auth_factor_01FZ4TS14D1PHFNZ9GF6YD8M1F',
102
+ sms_template: 'Your code is {{code}}',
103
+ )
104
+ expect(challenge_factor.authentication_factor_id.instance_of?(String))
105
+ end
106
+ end
107
+ end
108
+
109
+ context 'challenge with generic' do
110
+ it 'returns a valid challenge factor object for generic otp' do
111
+ VCR.use_cassette 'mfa/challenge_factor_generic_valid' do
112
+ challenge_factor = described_class.challenge_factor(
113
+ authentication_factor_id: 'auth_factor_01FZ4WMXXA09XF6NK1XMKNWB3M',
114
+ )
115
+ expect(challenge_factor.code.instance_of?(String))
116
+ end
117
+ end
118
+ end
119
+
120
+ context 'challenge with totp mssing authentication_factor_id' do
121
+ it 'returns argument error' do
122
+ expect do
123
+ described_class.challenge_factor
124
+ end.to raise_error(
125
+ ArgumentError,
126
+ "Incomplete arguments: 'authentication_factor_id' is a required argument",
127
+ )
128
+ end
129
+ end
130
+ end
131
+
132
+ describe '.verify_factor' do
133
+ it 'throws a warning' do
134
+ expect do
135
+ VCR.use_cassette 'mfa/verify_challenge_generic_valid' do
136
+ described_class.verify_factor(
137
+ authentication_challenge_id: 'auth_challenge_01FZ4YVRBMXP5ZM0A7BP4AJ12J',
138
+ code: '897792',
139
+ )
140
+ end
141
+ end.to output("[DEPRECATION] `verify_factor` is deprecated. Please use `verify_challenge` instead.\n").to_stderr
142
+ end
143
+
144
+ it 'calls verify_challenge' do
145
+ VCR.use_cassette 'mfa/verify_challenge_generic_valid' do
146
+ verify_factor = described_class.verify_factor(
147
+ authentication_challenge_id: 'auth_challenge_01FZ4YVRBMXP5ZM0A7BP4AJ12J',
148
+ code: '897792',
149
+ )
150
+ expect(verify_factor.valid == 'true')
151
+ end
152
+ end
153
+ end
154
+
155
+ describe '.verify_challenge' do
156
+ context 'with generic otp' do
157
+ context 'and the challenge has not been verified' do
158
+ it 'returns true if the code is correct' do
159
+ VCR.use_cassette 'mfa/verify_challenge_generic_valid' do
160
+ verify_challenge = described_class.verify_challenge(
161
+ authentication_challenge_id: 'auth_challenge_01FZ4YVRBMXP5ZM0A7BP4AJ12J',
162
+ code: '897792',
163
+ )
164
+ expect(verify_challenge.valid == 'true')
165
+ end
166
+ end
167
+
168
+ it 'returns false if the code is incorrect' do
169
+ VCR.use_cassette 'mfa/verify_challenge_generic_valid_is_false' do
170
+ verify_challenge = described_class.verify_challenge(
171
+ authentication_challenge_id: 'auth_challenge_01FZ4YVRBMXP5ZM0A7BP4AJ12J',
172
+ code: '897792',
173
+ )
174
+ expect(verify_challenge.valid == 'false')
175
+ end
176
+ end
177
+ end
178
+
179
+ context 'and the challenge has already been verified' do
180
+ it 'returns an error' do
181
+ VCR.use_cassette 'mfa/verify_challenge_generic_invalid' do
182
+ expect do
183
+ described_class.verify_challenge(
184
+ authentication_challenge_id: 'auth_challenge_01FZ4YVRBMXP5ZM0A7BP4AJ12J',
185
+ code: '897792',
186
+ )
187
+ end.to raise_error(WorkOSV2::InvalidRequestError)
188
+ end
189
+ end
190
+ end
191
+
192
+ context 'and the challenge has expired' do
193
+ it 'returns an error' do
194
+ VCR.use_cassette 'mfa/verify_challenge_generic_expired' do
195
+ expect do
196
+ described_class.verify_challenge(
197
+ authentication_challenge_id: 'auth_challenge_01FZ4YVRBMXP5ZM0A7BP4AJ12J',
198
+ code: '897792',
199
+ )
200
+ end.to raise_error(WorkOSV2::InvalidRequestError)
201
+ end
202
+ end
203
+ end
204
+
205
+ context 'with missing code argument' do
206
+ it 'returns an argument error' do
207
+ expect do
208
+ described_class.verify_challenge(
209
+ authentication_challenge_id: 'auth_challenge_01FZ4YVRBMXP5ZM0A7BP4AJ12J',
210
+ )
211
+ end.to raise_error(
212
+ ArgumentError,
213
+ "Incomplete arguments: 'authentication_challenge_id' and 'code' are required arguments",
214
+ )
215
+ end
216
+ end
217
+
218
+ context 'with missing authentication_challenge_id argument' do
219
+ it 'returns an error' do
220
+ expect do
221
+ described_class.verify_challenge(
222
+ code: '897792',
223
+ )
224
+ end.to raise_error(
225
+ ArgumentError,
226
+ "Incomplete arguments: 'authentication_challenge_id' and 'code' are required arguments",
227
+ )
228
+ end
229
+ end
230
+
231
+ context 'with missing code and authentication_challenge_id arguments' do
232
+ it 'returns an argument error' do
233
+ expect do
234
+ described_class.verify_challenge
235
+ end.to raise_error(
236
+ ArgumentError,
237
+ "Incomplete arguments: 'authentication_challenge_id' and 'code' are required arguments",
238
+ )
239
+ end
240
+ end
241
+ end
242
+ end
243
+
244
+ describe '.get_factor' do
245
+ context 'with a valid id' do
246
+ it 'returns a factor' do
247
+ VCR.use_cassette 'mfa/get_factor_valid' do
248
+ factor = described_class.get_factor(
249
+ id: 'auth_factor_01FZ4WMXXA09XF6NK1XMKNWB3M',
250
+ )
251
+ expect(factor.id.instance_of?(String))
252
+ end
253
+ end
254
+ end
255
+
256
+ context 'with an invalid id' do
257
+ it 'returns an error' do
258
+ VCR.use_cassette 'mfa/get_factor_invalid' do
259
+ expect do
260
+ described_class.get_factor(
261
+ id: 'auth_factor_invalid',
262
+ )
263
+ end.to raise_error(WorkOSV2::APIError)
264
+ end
265
+ end
266
+ end
267
+ end
268
+
269
+ describe '.delete_factor' do
270
+ context 'deletes facotr' do
271
+ it 'uses delete_factor to delete factor' do
272
+ VCR.use_cassette 'mfa/delete_factor' do
273
+ response = described_class.delete_factor(
274
+ id: 'auth_factor_01FZ4WMXXA09XF6NK1XMKNWB3M',
275
+ )
276
+ expect(response).to be(true)
277
+ end
278
+ end
279
+ end
280
+ end
281
+ end
@@ -0,0 +1,257 @@
1
+ # frozen_string_literal: true
2
+ # typed: false
3
+
4
+ describe WorkOSV2::Organizations do
5
+ it_behaves_like 'client'
6
+
7
+ describe '.create_organization' do
8
+ context 'with valid payload' do
9
+ context 'with no idempotency key' do
10
+ it 'creates an organization' do
11
+ VCR.use_cassette 'organization/create' do
12
+ organization = described_class.create_organization(
13
+ domains: ['example.io'],
14
+ name: 'Test Organization',
15
+ )
16
+
17
+ expect(organization.id).to eq('org_01FCPEJXEZR4DSBA625YMGQT9N')
18
+ expect(organization.name).to eq('Test Organization')
19
+ expect(organization.domains.first[:domain]).to eq('example.io')
20
+ end
21
+ end
22
+ end
23
+
24
+ context 'with idempotency key' do
25
+ context 'when idempotency key is used once' do
26
+ it 'creates an organization' do
27
+ VCR.use_cassette 'organization/create_with_idempotency_key' do
28
+ organization = described_class.create_organization(
29
+ domains: ['example.io'],
30
+ name: 'Test Organization',
31
+ idempotency_key: 'key',
32
+ )
33
+
34
+ expect(organization.name).to eq('Test Organization')
35
+ expect(organization.domains.first[:domain]).to eq('example.io')
36
+ end
37
+ end
38
+ end
39
+
40
+ context 'when idempotency key is used more than once' do
41
+ context 'with duplicate event payloads' do
42
+ it 'returns the already created organization' do
43
+ VCR.use_cassette 'organization/create_with_duplicate_idempotency_key_and_payload' do
44
+ organization1 = described_class.create_organization(
45
+ domains: ['example.com'],
46
+ name: 'Test Organization',
47
+ idempotency_key: 'foo',
48
+ )
49
+
50
+ organization2 = described_class.create_organization(
51
+ domains: ['example.com'],
52
+ name: 'Test Organization',
53
+ idempotency_key: 'foo',
54
+ )
55
+
56
+ expect(organization1.id).to eq(organization2.id)
57
+ end
58
+ end
59
+ end
60
+
61
+ context 'with different event payloads' do
62
+ it 'raises an error' do
63
+ VCR.use_cassette 'organization/create_with_duplicate_idempotency_key_and_different_payload' do
64
+ described_class.create_organization(
65
+ domains: ['example.me'],
66
+ name: 'Test Organization',
67
+ idempotency_key: 'bar',
68
+ )
69
+
70
+ expect do
71
+ described_class.create_organization(
72
+ domains: ['example.me'],
73
+ name: 'Organization Test',
74
+ idempotency_key: 'bar',
75
+ )
76
+ end.to raise_error(
77
+ WorkOSV2::InvalidRequestError,
78
+ /Status 400, Another idempotency key \(bar\) with different request parameters was found. Please use a different idempotency key./,
79
+ )
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ context 'with an invalid payload' do
88
+ it 'returns an error' do
89
+ VCR.use_cassette 'organization/create_invalid' do
90
+ expect do
91
+ described_class.create_organization(
92
+ domains: ['example.com'],
93
+ name: 'Test Organization 2',
94
+ )
95
+ end.to raise_error(
96
+ WorkOSV2::APIError,
97
+ /An Organization with the domain example.com already exists/,
98
+ )
99
+ end
100
+ end
101
+ end
102
+ end
103
+
104
+ describe '.list_organizations' do
105
+ context 'with no options' do
106
+ it 'returns organizations and metadata' do
107
+ expected_metadata = {
108
+ 'after' => nil,
109
+ 'before' => 'before-id',
110
+ }
111
+
112
+ VCR.use_cassette 'organization/list' do
113
+ organizations = described_class.list_organizations
114
+
115
+ expect(organizations.data.size).to eq(6)
116
+ expect(organizations.list_metadata).to eq(expected_metadata)
117
+ end
118
+ end
119
+ end
120
+
121
+ context 'with the before option' do
122
+ it 'forms the proper request to the API' do
123
+ request_args = [
124
+ '/organizations?before=before-id',
125
+ 'Content-Type' => 'application/json'
126
+ ]
127
+
128
+ expected_request = Net::HTTP::Get.new(*request_args)
129
+
130
+ expect(Net::HTTP::Get).to receive(:new).with(*request_args).
131
+ and_return(expected_request)
132
+
133
+ VCR.use_cassette 'organization/list', match_requests_on: [:path] do
134
+ organizations = described_class.list_organizations(
135
+ before: 'before-id',
136
+ )
137
+
138
+ expect(organizations.data.size).to eq(6)
139
+ end
140
+ end
141
+ end
142
+
143
+ context 'with the after option' do
144
+ it 'forms the proper request to the API' do
145
+ request_args = [
146
+ '/organizations?after=after-id',
147
+ 'Content-Type' => 'application/json'
148
+ ]
149
+
150
+ expected_request = Net::HTTP::Get.new(*request_args)
151
+
152
+ expect(Net::HTTP::Get).to receive(:new).with(*request_args).
153
+ and_return(expected_request)
154
+
155
+ VCR.use_cassette 'organization/list', match_requests_on: [:path] do
156
+ organizations = described_class.list_organizations(after: 'after-id')
157
+
158
+ expect(organizations.data.size).to eq(6)
159
+ end
160
+ end
161
+ end
162
+
163
+ context 'with the limit option' do
164
+ it 'forms the proper request to the API' do
165
+ request_args = [
166
+ '/organizations?limit=10',
167
+ 'Content-Type' => 'application/json'
168
+ ]
169
+
170
+ expected_request = Net::HTTP::Get.new(*request_args)
171
+
172
+ expect(Net::HTTP::Get).to receive(:new).with(*request_args).
173
+ and_return(expected_request)
174
+
175
+ VCR.use_cassette 'organization/list', match_requests_on: [:path] do
176
+ organizations = described_class.list_organizations(limit: 10)
177
+
178
+ expect(organizations.data.size).to eq(6)
179
+ end
180
+ end
181
+ end
182
+ end
183
+
184
+ describe '.get_organization' do
185
+ context 'with a valid id' do
186
+ it 'gets the organization details' do
187
+ VCR.use_cassette('organization/get') do
188
+ organization = described_class.get_organization(
189
+ id: 'org_01F9293WD2PDEEV4Y625XPZVG7',
190
+ )
191
+
192
+ expect(organization.id).to eq('org_01F9293WD2PDEEV4Y625XPZVG7')
193
+ expect(organization.name).to eq('Foo Corp')
194
+ expect(organization.domains.first[:domain]).to eq('foo-corp.com')
195
+ end
196
+ end
197
+ end
198
+
199
+ context 'with an invalid id' do
200
+ it 'raises an error' do
201
+ VCR.use_cassette('organization/get_invalid') do
202
+ expect do
203
+ described_class.get_organization(id: 'invalid')
204
+ end.to raise_error(
205
+ WorkOSV2::APIError,
206
+ 'Status 404, Not Found - request ID: ',
207
+ )
208
+ end
209
+ end
210
+ end
211
+ end
212
+
213
+ describe '.update_organization' do
214
+ context 'with valid payload' do
215
+ it 'creates an organization' do
216
+ VCR.use_cassette 'organization/update' do
217
+ organization = described_class.update_organization(
218
+ organization: 'org_01F6Q6TFP7RD2PF6J03ANNWDKV',
219
+ domains: ['example.me'],
220
+ name: 'Test Organization',
221
+ )
222
+
223
+ expect(organization.id).to eq('org_01F6Q6TFP7RD2PF6J03ANNWDKV')
224
+ expect(organization.name).to eq('Test Organization')
225
+ expect(organization.domains.first[:domain]).to eq('example.me')
226
+ end
227
+ end
228
+ end
229
+ end
230
+
231
+ describe '.delete_organization' do
232
+ context 'with a valid id' do
233
+ it 'returns true' do
234
+ VCR.use_cassette('organization/delete') do
235
+ response = described_class.delete_organization(
236
+ id: 'org_01F4A8TD0B4N1Y9SJ8SH635HDB',
237
+ )
238
+
239
+ expect(response).to be(true)
240
+ end
241
+ end
242
+ end
243
+
244
+ context 'with an invalid id' do
245
+ it 'returns false' do
246
+ VCR.use_cassette('organization/delete_invalid') do
247
+ expect do
248
+ described_class.delete_organization(id: 'invalid')
249
+ end.to raise_error(
250
+ WorkOSV2::APIError,
251
+ 'Status 404, Not Found - request ID: ',
252
+ )
253
+ end
254
+ end
255
+ end
256
+ end
257
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+ # typed: false
3
+
4
+ describe WorkOSV2::Passwordless do
5
+ it_behaves_like 'client'
6
+
7
+ describe '.create_session' do
8
+ context 'with valid options payload' do
9
+ let(:valid_options) do
10
+ {
11
+ email: 'demo@workos-okta.com',
12
+ type: 'MagicLink',
13
+ redirect_uri: 'foo.com/auth/callback',
14
+ }
15
+ end
16
+
17
+ it 'creates a session' do
18
+ VCR.use_cassette('passwordless/create_session') do
19
+ response = described_class.create_session(valid_options)
20
+
21
+ expect(response.email).to eq 'demo@workos-okta.com'
22
+ end
23
+ end
24
+ end
25
+
26
+ context 'with invalid event payload' do
27
+ let(:invalid_options) do
28
+ {}
29
+ end
30
+
31
+ it 'raises an error' do
32
+ VCR.use_cassette('passwordless/create_session_invalid') do
33
+ expect do
34
+ described_class.create_session(invalid_options)
35
+ end.to raise_error(
36
+ WorkOSV2::InvalidRequestError,
37
+ /Status 422, Validation failed \(email: email must be a string; type: type must be a valid enum value\)/,
38
+ )
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ describe '.send_session' do
45
+ context 'with valid session id' do
46
+ let(:valid_options) do
47
+ {
48
+ email: 'demo@workos-okta.com',
49
+ type: 'MagicLink',
50
+ }
51
+ end
52
+
53
+ it 'send a session' do
54
+ VCR.use_cassette('passwordless/send_session') do
55
+ response = described_class.send_session(
56
+ 'passwordless_session_01EJC0F4KH42T11Y2DHPEB09BM',
57
+ )
58
+
59
+ expect(response['success']).to eq true
60
+ end
61
+ end
62
+ end
63
+
64
+ context 'with invalid session id' do
65
+ it 'raises an error' do
66
+ VCR.use_cassette('passwordless/send_session_invalid') do
67
+ expect do
68
+ described_class.send_session('session_123')
69
+ end.to raise_error(
70
+ WorkOSV2::InvalidRequestError,
71
+ /Status 422, The passwordless session 'session_123' has expired or is invalid./,
72
+ )
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+ # typed: false
3
+
4
+ describe WorkOSV2::Portal do
5
+ it_behaves_like 'client'
6
+
7
+ describe '.generate_link' do
8
+ let(:organization) { 'org_01EHQMYV6MBK39QC5PZXHY59C3' }
9
+
10
+ describe 'with a valid organization' do
11
+ context 'with the sso intent' do
12
+ it 'returns an Admin Portal link' do
13
+ VCR.use_cassette 'portal/generate_link_sso' do
14
+ portal_link = described_class.generate_link(
15
+ intent: 'sso',
16
+ organization: organization,
17
+ )
18
+
19
+ expect(portal_link).to eq(
20
+ 'https://id.workos.com/portal/launch?secret=secret',
21
+ )
22
+ end
23
+ end
24
+ end
25
+
26
+ describe 'with the dsync intent' do
27
+ it 'returns an Admin Portal link' do
28
+ VCR.use_cassette 'portal/generate_link_dsync' do
29
+ portal_link = described_class.generate_link(
30
+ intent: 'dsync',
31
+ organization: organization,
32
+ )
33
+
34
+ expect(portal_link).to eq(
35
+ 'https://id.workos.com/portal/launch?secret=secret',
36
+ )
37
+ end
38
+ end
39
+ end
40
+
41
+ describe 'with the audit_logs intent' do
42
+ it 'returns an Admin Portal link' do
43
+ VCR.use_cassette 'portal/generate_link_audit_logs', match_requests_on: %i[path body] do
44
+ portal_link = described_class.generate_link(
45
+ intent: 'audit_logs',
46
+ organization: organization,
47
+ )
48
+
49
+ expect(portal_link).to eq(
50
+ 'https://id.workos.com/portal/launch?secret=secret',
51
+ )
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ describe 'with an invalid organization' do
58
+ it 'raises an error' do
59
+ VCR.use_cassette 'portal/generate_link_invalid' do
60
+ expect do
61
+ described_class.generate_link(
62
+ intent: 'sso',
63
+ organization: 'bogus-id',
64
+ )
65
+ end.to raise_error(
66
+ WorkOSV2::InvalidRequestError,
67
+ /Could not find an organization with the id, bogus-id/,
68
+ )
69
+ end
70
+ end
71
+ end
72
+
73
+ describe 'with an invalid intent' do
74
+ it 'raises an error' do
75
+ expect do
76
+ described_class.generate_link(
77
+ intent: 'bogus-intent',
78
+ organization: organization,
79
+ )
80
+ end.to raise_error(
81
+ ArgumentError,
82
+ /bogus-intent is not a valid value/,
83
+ )
84
+ end
85
+ end
86
+ end
87
+ end