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,178 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+
5
+ require 'net/http'
6
+ require 'uri'
7
+
8
+ module WorkOSV2
9
+ # The MFA module provides convenience methods for working with the WorkOSV2
10
+ # MFA platform. You'll need a valid API key
11
+ module MFA
12
+ class << self
13
+ extend T::Sig
14
+ include Client
15
+ sig { params(id: String).returns(T::Boolean) }
16
+ def delete_factor(id:)
17
+ response = execute_request(
18
+ request: delete_request(
19
+ path: "/auth/factors/#{id}",
20
+ auth: true,
21
+ ),
22
+ )
23
+ response.is_a? Net::HTTPSuccess
24
+ end
25
+
26
+ sig do
27
+ params(id: String).returns(WorkOSV2::Factor)
28
+ end
29
+ def get_factor(
30
+ id:
31
+ )
32
+ response = execute_request(
33
+ request: get_request(
34
+ path: "/auth/factors/#{id}",
35
+ auth: true,
36
+ ),
37
+ )
38
+ WorkOSV2::Factor.new(response.body)
39
+ end
40
+
41
+ sig do
42
+ params(
43
+ type: String,
44
+ totp_issuer: T.nilable(String),
45
+ totp_user: T.nilable(String),
46
+ phone_number: T.nilable(String),
47
+ ).void
48
+ end
49
+ # rubocop:disable Metrics/CyclomaticComplexity
50
+ # rubocop:disable Metrics/PerceivedComplexity
51
+
52
+ def validate_args(
53
+ type:,
54
+ totp_issuer: nil,
55
+ totp_user: nil,
56
+ phone_number: nil
57
+ )
58
+ if type != 'sms' && type != 'totp' && type != 'generic_otp'
59
+ raise ArgumentError, "Type argument must be either 'sms' or 'totp'"
60
+ end
61
+ if (type == 'totp' && totp_issuer.nil?) || (type == 'totp' && totp_user.nil?)
62
+ raise ArgumentError, 'Incomplete arguments. Need to specify both totp_issuer and totp_user when type is totp'
63
+ end
64
+ return unless type == 'sms' && phone_number.nil?
65
+
66
+ raise ArgumentError, 'Incomplete arguments. Need to specify phone_number when type is sms'
67
+ end
68
+ # rubocop:enable Metrics/CyclomaticComplexity
69
+ # rubocop:enable Metrics/PerceivedComplexity
70
+
71
+ sig do
72
+ params(
73
+ type: String,
74
+ totp_issuer: T.nilable(String),
75
+ totp_user: T.nilable(String),
76
+ phone_number: T.nilable(String),
77
+ ).returns(WorkOSV2::Factor)
78
+ end
79
+ def enroll_factor(
80
+ type:,
81
+ totp_issuer: nil,
82
+ totp_user: nil,
83
+ phone_number: nil
84
+ )
85
+ validate_args(
86
+ type: type,
87
+ totp_issuer: totp_issuer,
88
+ totp_user: totp_user,
89
+ phone_number: phone_number,
90
+ )
91
+ response = execute_request(request: post_request(
92
+ auth: true,
93
+ body: {
94
+ type: type,
95
+ totp_issuer: totp_issuer,
96
+ totp_user: totp_user,
97
+ phone_number: phone_number,
98
+ },
99
+ path: '/auth/factors/enroll',
100
+ ))
101
+ WorkOSV2::Factor.new(response.body)
102
+ end
103
+
104
+ sig do
105
+ params(
106
+ authentication_factor_id: T.nilable(String),
107
+ sms_template: T.nilable(String),
108
+ ).returns(WorkOSV2::Challenge)
109
+ end
110
+ def challenge_factor(
111
+ authentication_factor_id: nil,
112
+ sms_template: nil
113
+ )
114
+ if authentication_factor_id.nil?
115
+ raise ArgumentError, "Incomplete arguments: 'authentication_factor_id' is a required argument"
116
+ end
117
+
118
+ request = post_request(
119
+ auth: true,
120
+ body: {
121
+ sms_template: sms_template,
122
+ },
123
+ path: "/auth/factors/#{authentication_factor_id}/challenge",
124
+ )
125
+
126
+ response = execute_request(request: request)
127
+ WorkOSV2::Challenge.new(response.body)
128
+ end
129
+
130
+ sig do
131
+ params(
132
+ authentication_challenge_id: T.nilable(String),
133
+ code: T.nilable(String),
134
+ ).returns(WorkOSV2::VerifyChallenge)
135
+ end
136
+ def verify_factor(
137
+ authentication_challenge_id: nil,
138
+ code: nil
139
+ )
140
+ warn '[DEPRECATION] `verify_factor` is deprecated. Please use `verify_challenge` instead.'
141
+
142
+ verify_challenge(
143
+ authentication_challenge_id: authentication_challenge_id,
144
+ code: code,
145
+ )
146
+ end
147
+
148
+ sig do
149
+ params(
150
+ authentication_challenge_id: T.nilable(String),
151
+ code: T.nilable(String),
152
+ ).returns(WorkOSV2::VerifyChallenge)
153
+ end
154
+ def verify_challenge(
155
+ authentication_challenge_id: nil,
156
+ code: nil
157
+ )
158
+
159
+ if authentication_challenge_id.nil? || code.nil?
160
+ raise ArgumentError, "Incomplete arguments: 'authentication_challenge_id' and 'code' are required arguments"
161
+ end
162
+
163
+ options = {
164
+ "code": code,
165
+ }
166
+
167
+ response = execute_request(
168
+ request: post_request(
169
+ path: "/auth/challenges/#{authentication_challenge_id}/verify",
170
+ auth: true,
171
+ body: options,
172
+ ),
173
+ )
174
+ WorkOSV2::VerifyChallenge.new(response.body)
175
+ end
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ module WorkOSV2
5
+ # The Organization class provides a lightweight wrapper around
6
+ # a WorkOSV2 Organization resource. This class is not meant to be instantiated
7
+ # in user space, and is instantiated internally but exposed.
8
+ class Organization
9
+ include HashProvider
10
+ extend T::Sig
11
+
12
+ attr_accessor :id, :domains, :name, :allow_profiles_outside_organization, :created_at, :updated_at
13
+
14
+ sig { params(json: String).void }
15
+ def initialize(json)
16
+ raw = parse_json(json)
17
+
18
+ @id = T.let(raw.id, String)
19
+ @name = T.let(raw.name, String)
20
+ @allow_profiles_outside_organization = T.let(raw.allow_profiles_outside_organization, T::Boolean)
21
+ @domains = T.let(raw.domains, Array)
22
+ @created_at = T.let(raw.created_at, String)
23
+ @updated_at = T.let(raw.updated_at, String)
24
+ end
25
+
26
+ def to_json(*)
27
+ {
28
+ id: id,
29
+ name: name,
30
+ allow_profiles_outside_organization: allow_profiles_outside_organization,
31
+ domains: domains,
32
+ created_at: created_at,
33
+ updated_at: updated_at,
34
+ }
35
+ end
36
+
37
+ private
38
+
39
+ sig do
40
+ params(
41
+ json_string: String,
42
+ ).returns(WorkOSV2::Types::OrganizationStruct)
43
+ end
44
+ def parse_json(json_string)
45
+ hash = JSON.parse(json_string, symbolize_names: true)
46
+
47
+ WorkOSV2::Types::OrganizationStruct.new(
48
+ id: hash[:id],
49
+ name: hash[:name],
50
+ allow_profiles_outside_organization: hash[:allow_profiles_outside_organization],
51
+ domains: hash[:domains],
52
+ created_at: hash[:created_at],
53
+ updated_at: hash[:updated_at],
54
+ )
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,188 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ require 'net/http'
5
+
6
+ module WorkOSV2
7
+ # The Organizations module provides resource methods for working with Organizations
8
+ module Organizations
9
+ class << self
10
+ extend T::Sig
11
+ include Client
12
+
13
+ # Retrieve a list of organizations that have connections configured
14
+ # within your WorkOSV2 dashboard.
15
+ #
16
+ # @param [Array<String>] domains Filter organizations to only return those
17
+ # that are associated with the provided domains.
18
+ # @param [String] before A pagination argument used to request
19
+ # organizations before the provided Organization ID.
20
+ # @param [String] after A pagination argument used to request
21
+ # organizations after the provided Organization ID.
22
+ # @param [Integer] limit A pagination argument used to limit the number
23
+ # @param [String] order The order in which to paginate records
24
+ # of listed Organizations that are returned.
25
+ sig do
26
+ params(
27
+ options: T::Hash[Symbol, String],
28
+ ).returns(WorkOSV2::Types::ListStruct)
29
+ end
30
+ def list_organizations(options = {})
31
+ response = execute_request(
32
+ request: get_request(
33
+ path: '/organizations',
34
+ auth: true,
35
+ params: options,
36
+ ),
37
+ )
38
+
39
+ parsed_response = JSON.parse(response.body)
40
+
41
+ organizations = parsed_response['data'].map do |organization|
42
+ ::WorkOSV2::Organization.new(organization.to_json)
43
+ end
44
+
45
+ WorkOSV2::Types::ListStruct.new(
46
+ data: organizations,
47
+ list_metadata: parsed_response['listMetadata'],
48
+ )
49
+ end
50
+
51
+ # Get an Organization
52
+ #
53
+ # @param [String] id Organization unique identifier
54
+ #
55
+ # @example
56
+ # WorkOSV2::Portal.get_organization(id: 'org_02DRA1XNSJDZ19A31F183ECQW9')
57
+ # => #<WorkOSV2::Organization:0x00007fb6e4193d20
58
+ # @id="org_02DRA1XNSJDZ19A31F183ECQW9",
59
+ # @name="Foo Corp",
60
+ # @domains=
61
+ # [{:object=>"organization_domain",
62
+ # :id=>"org_domain_01E6PK9N3XMD8RHWF7S66380AR",
63
+ # :domain=>"foo-corp.com"}]>
64
+ #
65
+ # @return [WorkOSV2::Organization]
66
+ sig { params(id: String).returns(WorkOSV2::Organization) }
67
+ def get_organization(id:)
68
+ request = get_request(
69
+ auth: true,
70
+ path: "/organizations/#{id}",
71
+ )
72
+
73
+ response = execute_request(request: request)
74
+
75
+ WorkOSV2::Organization.new(response.body)
76
+ end
77
+
78
+ # Create an organization
79
+ #
80
+ # @param [Array<String>] domains List of domains that belong to the
81
+ # organization
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.
85
+ # @param [String] idempotency_key An idempotency key
86
+ sig do
87
+ params(
88
+ domains: T::Array[String],
89
+ name: String,
90
+ allow_profiles_outside_organization: T.nilable(T::Boolean),
91
+ idempotency_key: T.nilable(String),
92
+ ).returns(WorkOSV2::Organization)
93
+ end
94
+ def create_organization(domains:, name:, allow_profiles_outside_organization: nil, idempotency_key: nil)
95
+ request = post_request(
96
+ auth: true,
97
+ body: {
98
+ domains: domains,
99
+ name: name,
100
+ allow_profiles_outside_organization: allow_profiles_outside_organization,
101
+ },
102
+ path: '/organizations',
103
+ idempotency_key: idempotency_key,
104
+ )
105
+
106
+ response = execute_request(request: request)
107
+ check_and_raise_organization_error(response: response)
108
+
109
+ WorkOSV2::Organization.new(response.body)
110
+ end
111
+
112
+ # Update an organization
113
+ #
114
+ # @param [String] organization Organization unique identifier
115
+ # @param [Array<String>] domains List of domains that belong to the
116
+ # organization
117
+ # @param [String] name A unique, descriptive name for the organization
118
+ # @param [Boolean, nil] allow_profiles_outside_organization Whether Connections
119
+ # within the Organization allow profiles that are outside of the Organization's configured User Email Domains.
120
+ sig do
121
+ params(
122
+ organization: String,
123
+ domains: T::Array[String],
124
+ name: String,
125
+ allow_profiles_outside_organization: T.nilable(T::Boolean),
126
+ ).returns(WorkOSV2::Organization)
127
+ end
128
+ def update_organization(organization:, domains:, name:, allow_profiles_outside_organization: nil)
129
+ request = put_request(
130
+ auth: true,
131
+ body: {
132
+ domains: domains,
133
+ name: name,
134
+ allow_profiles_outside_organization: allow_profiles_outside_organization,
135
+ },
136
+ path: "/organizations/#{organization}",
137
+ )
138
+
139
+ response = execute_request(request: request)
140
+ check_and_raise_organization_error(response: response)
141
+
142
+ WorkOSV2::Organization.new(response.body)
143
+ end
144
+
145
+ # Delete an Organization
146
+ #
147
+ # @param [String] id Organization unique identifier
148
+ #
149
+ # @example
150
+ # WorkOSV2::SSO.delete_organization(id: 'org_01EHZNVPK3SFK441A1RGBFSHRT')
151
+ # => true
152
+ #
153
+ # @return [Bool] - returns `true` if successful
154
+ sig { params(id: String).returns(T::Boolean) }
155
+ def delete_organization(id:)
156
+ request = delete_request(
157
+ auth: true,
158
+ path: "/organizations/#{id}",
159
+ )
160
+
161
+ response = execute_request(request: request)
162
+
163
+ response.is_a? Net::HTTPSuccess
164
+ end
165
+
166
+ private
167
+
168
+ sig { params(response: Net::HTTPResponse).void }
169
+ def check_and_raise_organization_error(response:)
170
+ begin
171
+ body = JSON.parse(response.body)
172
+ return unless body['message']
173
+
174
+ message = body['message']
175
+ request_id = response['x-request-id']
176
+ rescue StandardError
177
+ message = 'Something went wrong'
178
+ end
179
+
180
+ raise APIError.new(
181
+ message: message,
182
+ http_status: nil,
183
+ request_id: request_id,
184
+ )
185
+ end
186
+ end
187
+ end
188
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ require 'net/http'
5
+
6
+ module WorkOSV2
7
+ # The Passwordless module provides convenience methods for working with
8
+ # passwordless sessions including the WorkOSV2 Magic Link. You'll need a valid
9
+ # API key.
10
+ #
11
+ # @see https://workos.com/docs/sso/configuring-magic-link
12
+ module Passwordless
13
+ class << self
14
+ extend T::Sig
15
+ include Client
16
+
17
+ # Create a Passwordless Session.
18
+ #
19
+ # @param [Hash] options A hash with options for the session
20
+ # @option options [String] email The email of the user to authenticate.
21
+ # @option options [String] state Optional parameter that the redirect URI
22
+ # received from WorkOSV2 will contain. The state parameter can be used to
23
+ # encode arbitrary information to help restore application state between
24
+ # redirects.
25
+ # @option options [String] connection Optional parameter for the ID of a
26
+ # specific connection. This can be used to create a Passwordless Session
27
+ # for a specific connection rather than using the domain from the email
28
+ # to determine the Organization and Connection.
29
+ # @option options [String] type The type of Passwordless Session to
30
+ # create. Currently, the only supported value is 'MagicLink'.
31
+ # @option options [String] redirect_uri The URI where users are directed
32
+ # after completing the authentication step. Must match a
33
+ # configured redirect URI on your WorkOSV2 dashboard.
34
+ #
35
+ # @return Hash
36
+ sig do
37
+ params(
38
+ options: Hash,
39
+ ).returns(WorkOSV2::Types::PasswordlessSessionStruct)
40
+ end
41
+
42
+ def create_session(options)
43
+ response = execute_request(
44
+ request: post_request(
45
+ path: '/passwordless/sessions',
46
+ auth: true,
47
+ body: options,
48
+ ),
49
+ )
50
+
51
+ hash = JSON.parse(response.body)
52
+
53
+ WorkOSV2::Types::PasswordlessSessionStruct.new(
54
+ id: hash['id'],
55
+ email: hash['email'],
56
+ expires_at: Date.parse(hash['expires_at']),
57
+ link: hash['link'],
58
+ )
59
+ end
60
+
61
+ # Send a Passwordless Session via email.
62
+ #
63
+ # @param [String] session_id The unique identifier of the Passwordless
64
+ # Session to send an email for.
65
+ #
66
+ # @return Hash
67
+ sig do
68
+ params(
69
+ session_id: String,
70
+ ).returns(T::Hash[String, T::Boolean])
71
+ end
72
+
73
+ def send_session(session_id)
74
+ response = execute_request(
75
+ request: post_request(
76
+ path: "/passwordless/sessions/#{session_id}/send",
77
+ auth: true,
78
+ ),
79
+ )
80
+
81
+ JSON.parse(response.body)
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ require 'net/http'
5
+
6
+ module WorkOSV2
7
+ # The Portal module provides resource methods for working with the Admin
8
+ # Portal product
9
+ module Portal
10
+ class << self
11
+ extend T::Sig
12
+ include Client
13
+
14
+ GENERATE_LINK_INTENTS = WorkOSV2::Types::Intent.values.map(&:serialize).
15
+ freeze
16
+
17
+ # Generate a link to grant access to an organization's Admin Portal
18
+ #
19
+ # @param [String] intent The access scope for the generated Admin Portal
20
+ # link. Valid values are: ["audit_logs", "dsync", "log_streams", "sso",]
21
+ # @param [String] organization The ID of the organization the Admin
22
+ # Portal link will be generated for.
23
+ # @param [String] The URL that the end user will be redirected to upon
24
+ # exiting the generated Admin Portal. If none is provided, the default
25
+ # redirect link set in your WorkOSV2 Dashboard will be used.
26
+ # @param [String] The URL to which WorkOSV2 will redirect users to upon
27
+ # successfully setting up Single Sign On or Directory Sync.
28
+ sig do
29
+ params(
30
+ intent: String,
31
+ organization: String,
32
+ return_url: T.nilable(String),
33
+ success_url: T.nilable(String),
34
+ ).returns(String)
35
+ end
36
+ def generate_link(intent:, organization:, return_url: nil, success_url: nil)
37
+ validate_intent(intent)
38
+
39
+ request = post_request(
40
+ auth: true,
41
+ body: {
42
+ intent: intent,
43
+ organization: organization,
44
+ return_url: return_url,
45
+ success_url: success_url,
46
+ },
47
+ path: '/portal/generate_link',
48
+ )
49
+
50
+ response = execute_request(request: request)
51
+
52
+ JSON.parse(response.body)['link']
53
+ end
54
+
55
+ private
56
+
57
+ sig { params(intent: String).void }
58
+ def validate_intent(intent)
59
+ return if GENERATE_LINK_INTENTS.include?(intent)
60
+
61
+ raise ArgumentError, "#{intent} is not a valid value." \
62
+ " `intent` must be in #{GENERATE_LINK_INTENTS}"
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ module WorkOSV2
5
+ # The Profile class provides a lighweight wrapper around
6
+ # a normalized response from the various IDPs WorkOSV2
7
+ # supports as part of the SSO integration. This class
8
+ # is not meant to be instantiated in user space, and
9
+ # is instantiated internally but exposed.
10
+ class Profile
11
+ include HashProvider
12
+ extend T::Sig
13
+
14
+ sig { returns(String) }
15
+ attr_accessor :id, :email, :first_name, :last_name, :groups, :organization_id,
16
+ :connection_id, :connection_type, :idp_id, :raw_attributes
17
+
18
+ # rubocop:disable Metrics/AbcSize
19
+ sig { params(profile_json: String).void }
20
+ def initialize(profile_json)
21
+ raw = parse_json(profile_json)
22
+
23
+ @id = T.let(raw.id, String)
24
+ @email = T.let(raw.email, String)
25
+ @first_name = raw.first_name
26
+ @last_name = raw.last_name
27
+ @groups = raw.groups
28
+ @organization_id = raw.organization_id
29
+ @connection_id = T.let(raw.connection_id, String)
30
+ @connection_type = T.let(raw.connection_type, String)
31
+ @idp_id = raw.idp_id
32
+ @raw_attributes = raw.raw_attributes
33
+ end
34
+ # rubocop:enable Metrics/AbcSize
35
+
36
+ sig { returns(String) }
37
+ def full_name
38
+ [first_name, last_name].compact.join(' ')
39
+ end
40
+
41
+ def to_json(*)
42
+ {
43
+ id: id,
44
+ email: email,
45
+ first_name: first_name,
46
+ last_name: last_name,
47
+ groups: groups,
48
+ organization_id: organization_id,
49
+ connection_id: connection_id,
50
+ connection_type: connection_type,
51
+ idp_id: idp_id,
52
+ raw_attributes: raw_attributes,
53
+ }
54
+ end
55
+
56
+ private
57
+
58
+ sig { params(json_string: String).returns(WorkOSV2::Types::ProfileStruct) }
59
+ def parse_json(json_string)
60
+ hash = JSON.parse(json_string, symbolize_names: true)
61
+
62
+ WorkOSV2::Types::ProfileStruct.new(
63
+ id: hash[:id],
64
+ email: hash[:email],
65
+ first_name: hash[:first_name],
66
+ last_name: hash[:last_name],
67
+ groups: hash[:groups],
68
+ organization_id: hash[:organization_id],
69
+ connection_id: hash[:connection_id],
70
+ connection_type: hash[:connection_type],
71
+ idp_id: hash[:idp_id],
72
+ raw_attributes: hash[:raw_attributes],
73
+ )
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ module WorkOSV2
5
+ # The ProfileAndToken class represents a Profile and a corresponding
6
+ # Access Token. This class is not meant to be instantiated in user space, and
7
+ # is instantiated internally but exposed.
8
+ class ProfileAndToken
9
+ include HashProvider
10
+ extend T::Sig
11
+
12
+ attr_accessor :access_token, :profile
13
+
14
+ sig { params(profile_and_token_json: String).void }
15
+ def initialize(profile_and_token_json)
16
+ json = JSON.parse(profile_and_token_json, symbolize_names: true)
17
+
18
+ @access_token = T.let(json[:access_token], String)
19
+ @profile = WorkOSV2::Profile.new(json[:profile].to_json)
20
+ end
21
+
22
+ def to_json(*)
23
+ {
24
+ access_token: access_token,
25
+ profile: profile.to_json,
26
+ }
27
+ end
28
+ end
29
+ end