descope 1.0.4

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 (197) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/ci.yaml +54 -0
  3. data/.gitignore +59 -0
  4. data/.release-please-manifest.json +3 -0
  5. data/.rubocop.yml +10 -0
  6. data/.rubocop_todo.yml +10 -0
  7. data/.ruby-version +1 -0
  8. data/CHANGELOG.md +90 -0
  9. data/Gemfile +22 -0
  10. data/Gemfile.lock +204 -0
  11. data/LICENSE +21 -0
  12. data/README.md +1171 -0
  13. data/Rakefile +31 -0
  14. data/descope.gemspec +34 -0
  15. data/examples/ruby/Gemfile +4 -0
  16. data/examples/ruby/Gemfile.lock +41 -0
  17. data/examples/ruby/access_key_app.rb +45 -0
  18. data/examples/ruby/enchantedlink_app.rb +65 -0
  19. data/examples/ruby/magiclink_app.rb +81 -0
  20. data/examples/ruby/management/Gemfile +5 -0
  21. data/examples/ruby/management/Gemfile.lock +38 -0
  22. data/examples/ruby/management/access_key_app.rb +71 -0
  23. data/examples/ruby/management/audit_app.rb +25 -0
  24. data/examples/ruby/management/authz_app.rb +135 -0
  25. data/examples/ruby/management/authz_files.json +229 -0
  26. data/examples/ruby/management/flow_app.rb +57 -0
  27. data/examples/ruby/management/permission_app.rb +56 -0
  28. data/examples/ruby/management/role_app.rb +58 -0
  29. data/examples/ruby/management/tenant_app.rb +60 -0
  30. data/examples/ruby/management/user_app.rb +60 -0
  31. data/examples/ruby/oauth_app.rb +39 -0
  32. data/examples/ruby/otp_app.rb +50 -0
  33. data/examples/ruby/password_app.rb +76 -0
  34. data/examples/ruby/saml_app.rb +38 -0
  35. data/examples/ruby-on-rails-api/descope/.dockerignore +37 -0
  36. data/examples/ruby-on-rails-api/descope/.gitattributes +9 -0
  37. data/examples/ruby-on-rails-api/descope/.gitignore +40 -0
  38. data/examples/ruby-on-rails-api/descope/.node-version +1 -0
  39. data/examples/ruby-on-rails-api/descope/.ruby-version +1 -0
  40. data/examples/ruby-on-rails-api/descope/Dockerfile +75 -0
  41. data/examples/ruby-on-rails-api/descope/Gemfile +67 -0
  42. data/examples/ruby-on-rails-api/descope/Gemfile.lock +284 -0
  43. data/examples/ruby-on-rails-api/descope/Procfile.dev +3 -0
  44. data/examples/ruby-on-rails-api/descope/README.md +54 -0
  45. data/examples/ruby-on-rails-api/descope/Rakefile +6 -0
  46. data/examples/ruby-on-rails-api/descope/app/assets/builds/.keep +0 -0
  47. data/examples/ruby-on-rails-api/descope/app/assets/config/manifest.js +3 -0
  48. data/examples/ruby-on-rails-api/descope/app/assets/images/.keep +0 -0
  49. data/examples/ruby-on-rails-api/descope/app/assets/images/descope.jpeg +0 -0
  50. data/examples/ruby-on-rails-api/descope/app/assets/images/favicon.ico +0 -0
  51. data/examples/ruby-on-rails-api/descope/app/assets/images/logo192.png +0 -0
  52. data/examples/ruby-on-rails-api/descope/app/assets/images/logo512.png +0 -0
  53. data/examples/ruby-on-rails-api/descope/app/assets/stylesheets/application.bootstrap.scss +67 -0
  54. data/examples/ruby-on-rails-api/descope/app/channels/application_cable/channel.rb +4 -0
  55. data/examples/ruby-on-rails-api/descope/app/channels/application_cable/connection.rb +4 -0
  56. data/examples/ruby-on-rails-api/descope/app/controllers/application_controller.rb +2 -0
  57. data/examples/ruby-on-rails-api/descope/app/controllers/concerns/.keep +0 -0
  58. data/examples/ruby-on-rails-api/descope/app/controllers/homepage_controller.rb +4 -0
  59. data/examples/ruby-on-rails-api/descope/app/controllers/session_controller.rb +66 -0
  60. data/examples/ruby-on-rails-api/descope/app/helpers/application_helper.rb +2 -0
  61. data/examples/ruby-on-rails-api/descope/app/helpers/homepage_helper.rb +2 -0
  62. data/examples/ruby-on-rails-api/descope/app/helpers/session_helper.rb +2 -0
  63. data/examples/ruby-on-rails-api/descope/app/javascript/App.css +53 -0
  64. data/examples/ruby-on-rails-api/descope/app/javascript/application.js +5 -0
  65. data/examples/ruby-on-rails-api/descope/app/javascript/components/App.jsx +4 -0
  66. data/examples/ruby-on-rails-api/descope/app/javascript/components/Dashboard.jsx +60 -0
  67. data/examples/ruby-on-rails-api/descope/app/javascript/components/Home.jsx +27 -0
  68. data/examples/ruby-on-rails-api/descope/app/javascript/components/Login.jsx +45 -0
  69. data/examples/ruby-on-rails-api/descope/app/javascript/components/Profile.jsx +81 -0
  70. data/examples/ruby-on-rails-api/descope/app/javascript/components/index.html +11 -0
  71. data/examples/ruby-on-rails-api/descope/app/javascript/components/index.jsx +24 -0
  72. data/examples/ruby-on-rails-api/descope/app/javascript/controllers/application.js +9 -0
  73. data/examples/ruby-on-rails-api/descope/app/javascript/controllers/index.js +5 -0
  74. data/examples/ruby-on-rails-api/descope/app/javascript/reportWebVitals.js +13 -0
  75. data/examples/ruby-on-rails-api/descope/app/javascript/routes/index.jsx +17 -0
  76. data/examples/ruby-on-rails-api/descope/app/jobs/application_job.rb +7 -0
  77. data/examples/ruby-on-rails-api/descope/app/mailers/application_mailer.rb +4 -0
  78. data/examples/ruby-on-rails-api/descope/app/models/application_record.rb +3 -0
  79. data/examples/ruby-on-rails-api/descope/app/models/concerns/.keep +0 -0
  80. data/examples/ruby-on-rails-api/descope/app/views/homepage/index.html.erb +2 -0
  81. data/examples/ruby-on-rails-api/descope/app/views/layouts/application.html.erb +16 -0
  82. data/examples/ruby-on-rails-api/descope/app/views/layouts/mailer.html.erb +13 -0
  83. data/examples/ruby-on-rails-api/descope/app/views/layouts/mailer.text.erb +1 -0
  84. data/examples/ruby-on-rails-api/descope/app/views/session/index.html.erb +2 -0
  85. data/examples/ruby-on-rails-api/descope/bin/bundle +109 -0
  86. data/examples/ruby-on-rails-api/descope/bin/dev +11 -0
  87. data/examples/ruby-on-rails-api/descope/bin/docker-entrypoint +8 -0
  88. data/examples/ruby-on-rails-api/descope/bin/rails +4 -0
  89. data/examples/ruby-on-rails-api/descope/bin/rake +4 -0
  90. data/examples/ruby-on-rails-api/descope/bin/setup +36 -0
  91. data/examples/ruby-on-rails-api/descope/build.js +30 -0
  92. data/examples/ruby-on-rails-api/descope/config/application.rb +42 -0
  93. data/examples/ruby-on-rails-api/descope/config/boot.rb +4 -0
  94. data/examples/ruby-on-rails-api/descope/config/cable.yml +10 -0
  95. data/examples/ruby-on-rails-api/descope/config/config.yml +9 -0
  96. data/examples/ruby-on-rails-api/descope/config/credentials.yml.enc +1 -0
  97. data/examples/ruby-on-rails-api/descope/config/database.yml +25 -0
  98. data/examples/ruby-on-rails-api/descope/config/environment.rb +5 -0
  99. data/examples/ruby-on-rails-api/descope/config/environments/development.rb +76 -0
  100. data/examples/ruby-on-rails-api/descope/config/environments/production.rb +97 -0
  101. data/examples/ruby-on-rails-api/descope/config/environments/test.rb +64 -0
  102. data/examples/ruby-on-rails-api/descope/config/initializers/assets.rb +13 -0
  103. data/examples/ruby-on-rails-api/descope/config/initializers/content_security_policy.rb +25 -0
  104. data/examples/ruby-on-rails-api/descope/config/initializers/filter_parameter_logging.rb +8 -0
  105. data/examples/ruby-on-rails-api/descope/config/initializers/inflections.rb +16 -0
  106. data/examples/ruby-on-rails-api/descope/config/initializers/load_config.rb +12 -0
  107. data/examples/ruby-on-rails-api/descope/config/initializers/permissions_policy.rb +13 -0
  108. data/examples/ruby-on-rails-api/descope/config/locales/en.yml +31 -0
  109. data/examples/ruby-on-rails-api/descope/config/puma.rb +35 -0
  110. data/examples/ruby-on-rails-api/descope/config/routes.rb +18 -0
  111. data/examples/ruby-on-rails-api/descope/config/storage.yml +34 -0
  112. data/examples/ruby-on-rails-api/descope/config.ru +6 -0
  113. data/examples/ruby-on-rails-api/descope/db/seeds.rb +9 -0
  114. data/examples/ruby-on-rails-api/descope/lib/assets/.keep +0 -0
  115. data/examples/ruby-on-rails-api/descope/lib/tasks/.keep +0 -0
  116. data/examples/ruby-on-rails-api/descope/log/.keep +0 -0
  117. data/examples/ruby-on-rails-api/descope/package-lock.json +19680 -0
  118. data/examples/ruby-on-rails-api/descope/package.json +51 -0
  119. data/examples/ruby-on-rails-api/descope/public/404.html +67 -0
  120. data/examples/ruby-on-rails-api/descope/public/422.html +67 -0
  121. data/examples/ruby-on-rails-api/descope/public/500.html +66 -0
  122. data/examples/ruby-on-rails-api/descope/public/apple-touch-icon-precomposed.png +0 -0
  123. data/examples/ruby-on-rails-api/descope/public/apple-touch-icon.png +0 -0
  124. data/examples/ruby-on-rails-api/descope/public/favicon.ico +0 -0
  125. data/examples/ruby-on-rails-api/descope/public/robots.txt +1 -0
  126. data/examples/ruby-on-rails-api/descope/storage/.keep +0 -0
  127. data/examples/ruby-on-rails-api/descope/tmp/.keep +0 -0
  128. data/examples/ruby-on-rails-api/descope/tmp/pids/.keep +0 -0
  129. data/examples/ruby-on-rails-api/descope/tmp/storage/.keep +0 -0
  130. data/examples/ruby-on-rails-api/descope/vendor/.keep +0 -0
  131. data/examples/ruby-on-rails-api/descope/yarn.lock +10780 -0
  132. data/lib/descope/api/v1/auth/enchantedlink.rb +156 -0
  133. data/lib/descope/api/v1/auth/magiclink.rb +170 -0
  134. data/lib/descope/api/v1/auth/oauth.rb +72 -0
  135. data/lib/descope/api/v1/auth/otp.rb +186 -0
  136. data/lib/descope/api/v1/auth/password.rb +100 -0
  137. data/lib/descope/api/v1/auth/saml.rb +48 -0
  138. data/lib/descope/api/v1/auth/totp.rb +72 -0
  139. data/lib/descope/api/v1/auth.rb +452 -0
  140. data/lib/descope/api/v1/management/access_key.rb +81 -0
  141. data/lib/descope/api/v1/management/audit.rb +82 -0
  142. data/lib/descope/api/v1/management/authz.rb +165 -0
  143. data/lib/descope/api/v1/management/common.rb +147 -0
  144. data/lib/descope/api/v1/management/flow.rb +55 -0
  145. data/lib/descope/api/v1/management/password.rb +58 -0
  146. data/lib/descope/api/v1/management/permission.rb +48 -0
  147. data/lib/descope/api/v1/management/project.rb +53 -0
  148. data/lib/descope/api/v1/management/role.rb +48 -0
  149. data/lib/descope/api/v1/management/scim.rb +206 -0
  150. data/lib/descope/api/v1/management/sso_settings.rb +153 -0
  151. data/lib/descope/api/v1/management/tenant.rb +71 -0
  152. data/lib/descope/api/v1/management/user.rb +619 -0
  153. data/lib/descope/api/v1/management.rb +38 -0
  154. data/lib/descope/api/v1/session.rb +84 -0
  155. data/lib/descope/api/v1.rb +13 -0
  156. data/lib/descope/client.rb +6 -0
  157. data/lib/descope/exception.rb +50 -0
  158. data/lib/descope/mixins/common.rb +129 -0
  159. data/lib/descope/mixins/headers.rb +15 -0
  160. data/lib/descope/mixins/http.rb +133 -0
  161. data/lib/descope/mixins/initializer.rb +80 -0
  162. data/lib/descope/mixins/logging.rb +30 -0
  163. data/lib/descope/mixins/validation.rb +79 -0
  164. data/lib/descope/mixins.rb +22 -0
  165. data/lib/descope/version.rb +7 -0
  166. data/lib/descope.rb +9 -0
  167. data/lib/descope_client.rb +5 -0
  168. data/release-please-config.json +18 -0
  169. data/renovate.json +6 -0
  170. data/spec/factories/user.rb +16 -0
  171. data/spec/lib.descope/api/v1/auth/enchantedlink_spec.rb +159 -0
  172. data/spec/lib.descope/api/v1/auth/magiclink_spec.rb +282 -0
  173. data/spec/lib.descope/api/v1/auth/oauth_spec.rb +117 -0
  174. data/spec/lib.descope/api/v1/auth/otp_spec.rb +285 -0
  175. data/spec/lib.descope/api/v1/auth/password_spec.rb +124 -0
  176. data/spec/lib.descope/api/v1/auth/saml_spec.rb +55 -0
  177. data/spec/lib.descope/api/v1/auth/totp_spec.rb +70 -0
  178. data/spec/lib.descope/api/v1/auth_spec.rb +372 -0
  179. data/spec/lib.descope/api/v1/management/access_key_spec.rb +118 -0
  180. data/spec/lib.descope/api/v1/management/audit_spec.rb +78 -0
  181. data/spec/lib.descope/api/v1/management/authz_spec.rb +336 -0
  182. data/spec/lib.descope/api/v1/management/flow_spec.rb +78 -0
  183. data/spec/lib.descope/api/v1/management/password_spec.rb +25 -0
  184. data/spec/lib.descope/api/v1/management/permission_spec.rb +81 -0
  185. data/spec/lib.descope/api/v1/management/project_spec.rb +63 -0
  186. data/spec/lib.descope/api/v1/management/role_spec.rb +85 -0
  187. data/spec/lib.descope/api/v1/management/scim_spec.rb +312 -0
  188. data/spec/lib.descope/api/v1/management/sso_settings_spec.rb +172 -0
  189. data/spec/lib.descope/api/v1/management/tenant_spec.rb +141 -0
  190. data/spec/lib.descope/api/v1/management/user_spec.rb +667 -0
  191. data/spec/lib.descope/api/v1/session_spec.rb +117 -0
  192. data/spec/lib.descope/client_spec.rb +40 -0
  193. data/spec/spec_helper.rb +72 -0
  194. data/spec/support/client_config.rb +14 -0
  195. data/spec/support/dummy_class.rb +36 -0
  196. data/spec/support/utils.rb +32 -0
  197. metadata +420 -0
@@ -0,0 +1,156 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Descope
4
+ module Api
5
+ module V1
6
+ module Auth
7
+ # Holds all the password API calls
8
+ module EnchantedLink
9
+ include Descope::Mixins::Validation
10
+ include Descope::Mixins::Common::EndpointsV1
11
+ include Descope::Mixins::Common::EndpointsV2
12
+
13
+ def enchanted_link_sign_in(login_id: nil, uri: nil, login_options: nil, refresh_token: nil)
14
+ # Sign-in existing user by sending an enchanted link via email.
15
+ # @see https://docs.descope.com/api/openapi/enchantedlink/operation/SignInEnchantedLinkEmail/
16
+ validate_login_id(login_id)
17
+ validate_refresh_token_provided(login_options, refresh_token)
18
+ body = enchanted_link_compose_signin_body(login_id, uri, login_options)
19
+ uri = enchanted_link_compose_signin_url
20
+ post(uri, body, nil, refresh_token)
21
+ end
22
+
23
+ def enchanted_link_sign_up(login_id: nil, uri: nil, user: {})
24
+ # Sign-up new end user by sending an enchanted link via email
25
+ # @see https://docs.descope.com/api/openapi/enchantedlink/operation/SignUpEnchantedLink/
26
+
27
+ unless adjust_and_verify_delivery_method(Descope::Mixins::Common::DeliveryMethod::EMAIL, login_id, user)
28
+ raise Descope::ArgumentException.new(
29
+ 'Invalid delivery method',
30
+ code: 400
31
+ )
32
+ end
33
+
34
+ body = enchanted_link_compose_signup_body(login_id, uri, user)
35
+ uri = enchanted_link_compose_signup_url
36
+ post(uri, body)
37
+ end
38
+
39
+ def enchanted_link_sign_up_or_in(login_id: nil, uri: nil, login_options: nil)
40
+ # @see https://docs.descope.com/api/openapi/enchantedlink/operation/SignUpOrInEnchantedLinkEmail/
41
+ body = enchanted_link_compose_signin_body(login_id, uri, login_options)
42
+ uri = enchanted_link_compose_sign_up_or_in_url
43
+ post(uri, body)
44
+ end
45
+
46
+ def enchanted_link_update_user_email(login_id: nil, email: nil, uri: nil, add_to_login_ids: nil, on_merge_use_existing: nil, provider_id: nil, template_id: nil, template_options: nil, refresh_token: nil)
47
+ validate_login_id(login_id)
48
+ validate_token_not_empty(refresh_token)
49
+ validate_email(email)
50
+
51
+ body = enchanted_link_compose_update_user_email_body(
52
+ login_id, email, add_to_login_ids, on_merge_use_existing
53
+ )
54
+ body[:redirectUrl] = uri
55
+ body[:providerId] = provider_id if provider_id
56
+ body[:templateId] = template_id if template_id
57
+ body[:templateOptions] = template_options if template_options
58
+ uri = UPDATE_USER_EMAIL_ENCHANTEDLINK_PATH
59
+ post(uri, body, {}, refresh_token)
60
+ end
61
+
62
+ def enchanted_link_verify_token(token = nil)
63
+ validate_token_not_empty(token)
64
+ post(VERIFY_ENCHANTEDLINK_AUTH_PATH, { token: })
65
+ end
66
+
67
+ def enchanted_link_get_session(pending_ref = nil)
68
+ # @see https://docs.descope.com/api/openapi/enchantedlink/operation/GetEnchantedLinkSession/
69
+ res = post(GET_SESSION_ENCHANTEDLINK_AUTH_PATH, { pendingRef: pending_ref })
70
+ generate_jwt_response(response_body: res, refresh_cookie: res['refreshJwt'])
71
+ end
72
+
73
+ private
74
+
75
+ def enchanted_link_compose_signin_body(login_id, uri, login_options)
76
+ login_options ||= {}
77
+ unless login_options.is_a?(Hash)
78
+ raise Descope::ArgumentException.new(
79
+ 'Unable to read login_option, not a Hash',
80
+ code: 400
81
+ )
82
+ end
83
+
84
+ body = {
85
+ loginId: login_id,
86
+ redirectUrl: uri,
87
+ loginOptions: {}
88
+ }
89
+
90
+ body[:loginOptions][:stepup] = login_options.fetch(:stepup, false)
91
+ body[:loginOptions][:mfa] = login_options.fetch(:mfa, false)
92
+ body[:loginOptions][:customClaims] = login_options.fetch(:custom_claims, {})
93
+ body[:loginOptions][:ssoAppId] = login_options.fetch(:sso_app_id, nil)
94
+
95
+ body
96
+ end
97
+
98
+ def enchanted_link_compose_signin_url
99
+ compose_url(SIGN_IN_AUTH_ENCHANTEDLINK_PATH, Descope::Mixins::Common::DeliveryMethod::EMAIL)
100
+ end
101
+
102
+ def enchanted_link_compose_signup_url
103
+ compose_url(SIGN_UP_AUTH_ENCHANTEDLINK_PATH, Descope::Mixins::Common::DeliveryMethod::EMAIL)
104
+ end
105
+
106
+ def enchanted_link_compose_sign_up_or_in_url
107
+ compose_url(SIGN_UP_OR_IN_AUTH_ENCHANTEDLINK_PATH, Descope::Mixins::Common::DeliveryMethod::EMAIL)
108
+ end
109
+
110
+ def enchanted_link_compose_signup_body(login_id, uri, user)
111
+ body = {
112
+ loginId: login_id,
113
+ redirectUrl: uri
114
+ }
115
+
116
+ unless user.nil? || user.empty?
117
+ body[:user] = enchantedlink_user_compose_update_body(**user) unless user.empty?
118
+
119
+ method_str, val = get_login_id_by_method(method: Descope::Mixins::Common::DeliveryMethod::EMAIL, user:)
120
+ body[method_str.to_sym] = val
121
+ end
122
+
123
+ body
124
+ end
125
+
126
+ def enchanted_link_compose_update_user_email_body(login_id, email, add_to_login_ids, on_merge_use_existing)
127
+ body = {
128
+ loginId: login_id,
129
+ email:
130
+ }
131
+
132
+ body[:addToLoginIds] = add_to_login_ids if add_to_login_ids
133
+ body[:onMergeUseExisting] = on_merge_use_existing if on_merge_use_existing
134
+
135
+ body
136
+ end
137
+
138
+
139
+ private
140
+ def enchantedlink_user_compose_update_body(login_id: nil, name: nil, phone: nil, email: nil, given_name: nil, middle_name: nil, family_name: nil)
141
+ user = {}
142
+ user[:loginId] = login_id if login_id
143
+ user[:name] = name if name
144
+ user[:phone] = phone if phone
145
+ user[:email] = email if email
146
+ user[:givenName] = given_name if given_name
147
+ user[:middleName] = middle_name if middle_name
148
+ user[:familyName] = family_name if family_name
149
+
150
+ user
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,170 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Descope
4
+ module Api
5
+ module V1
6
+ module Auth
7
+ # Holds all the password API calls
8
+ module MagicLink
9
+ include Descope::Mixins::Validation
10
+ include Descope::Mixins::Common::EndpointsV1
11
+ include Descope::Mixins::Common::EndpointsV2
12
+
13
+ def magiclink_sign_up(method: nil, login_id: nil, uri: nil, user: {}, provider_id: nil, template_id: nil)
14
+ # Sign-up new end user by sending a magic link via email
15
+ # @see https://docs.descope.com/api/openapi/magiclink/operation/SignUpMagicLinkEmail/
16
+ validate_login_id(login_id)
17
+
18
+ body = magiclink_compose_signup_body(login_id, uri, user, method)
19
+ body[:providerId] = provider_id if provider_id
20
+ body[:templateId] = template_id if template_id
21
+ uri = magiclink_compose_signup_url(method)
22
+ res = post(uri, body)
23
+ extract_masked_address(res, method)
24
+ end
25
+
26
+ def magiclink_sign_in(method: nil, login_id: nil, uri: nil, login_options: nil, refresh_token: nil)
27
+ validate_login_id(login_id)
28
+ validate_refresh_token_provided(login_options, refresh_token)
29
+ body = magiclink_compose_signin_body(login_id, uri, login_options)
30
+ uri = magiclink_compose_signin_url(method)
31
+ res = post(uri, body, {}, refresh_token)
32
+ extract_masked_address(res, method)
33
+ end
34
+
35
+ def magiclink_sign_up_or_in(method: nil, login_id: nil, uri: nil, login_options: nil)
36
+ body = magiclink_compose_signin_body(login_id, uri, login_options)
37
+ uri = magiclink_compose_sign_up_or_in_url(method)
38
+ res = post(uri, body)
39
+ extract_masked_address(res, method)
40
+ end
41
+
42
+ def magiclink_verify_token(token = nil)
43
+ validate_token_not_empty(token)
44
+ res = post(VERIFY_MAGICLINK_AUTH_PATH, { token: })
45
+ generate_jwt_response(response_body: res)
46
+ end
47
+
48
+ def magiclink_update_user_email(login_id: nil, email: nil, uri: nil, add_to_login_ids: nil, on_merge_use_existing: nil, provider_id: nil, template_id: nil, template_options: nil, refresh_token: nil)
49
+ validate_login_id(login_id)
50
+ validate_token_not_empty(refresh_token)
51
+ validate_email(email)
52
+
53
+ body = magiclink_compose_update_user_email_body(login_id, email, uri, add_to_login_ids, on_merge_use_existing)
54
+ body[:providerId] = provider_id if provider_id
55
+ body[:templateId] = template_id if template_id
56
+ body[:templateOptions] = template_options if template_options
57
+ uri = UPDATE_USER_EMAIL_MAGICLINK_PATH
58
+ res = post(uri, body, {}, refresh_token)
59
+ extract_masked_address(res, Descope::Mixins::Common::DeliveryMethod::EMAIL)
60
+ end
61
+
62
+ def magiclink_update_user_phone(login_id: nil, phone: nil, uri: nil, add_to_login_ids: nil, on_merge_use_existing: nil, provider_id: nil, template_id: nil, template_options: nil, method: nil, refresh_token: nil)
63
+ validate_login_id(login_id)
64
+ validate_token_not_empty(refresh_token)
65
+ validate_phone(method, phone)
66
+
67
+ body = magiclink_compose_update_user_phone_body(login_id, phone, uri, add_to_login_ids, on_merge_use_existing)
68
+ body[:providerId] = provider_id if provider_id
69
+ body[:templateId] = template_id if template_id
70
+ body[:templateOptions] = template_options if template_options
71
+ uri = UPDATE_USER_PHONE_MAGICLINK_PATH
72
+ res = post(uri, body, {}, refresh_token)
73
+ extract_masked_address(res, Descope::Mixins::Common::DeliveryMethod::SMS)
74
+ end
75
+
76
+ private
77
+
78
+ def magiclink_compose_signin_url(method = nil)
79
+ method = Descope::Mixins::Common::DeliveryMethod::EMAIL if method.nil?
80
+ compose_url(SIGN_IN_AUTH_MAGICLINK_PATH, method)
81
+ end
82
+
83
+ def magiclink_compose_signup_url(method = nil)
84
+ method = Descope::Mixins::Common::DeliveryMethod::EMAIL if method.nil?
85
+ compose_url(SIGN_UP_AUTH_MAGICLINK_PATH, method)
86
+ end
87
+
88
+ def magiclink_compose_sign_up_or_in_url(method = nil)
89
+ method = Descope::Mixins::Common::DeliveryMethod::EMAIL if method.nil?
90
+ compose_url(SIGN_UP_OR_IN_AUTH_MAGICLINK_PATH, method)
91
+ end
92
+
93
+ def magiclink_compose_signup_body(login_id, uri, user = nil, method = nil)
94
+ body = {
95
+ loginId: login_id,
96
+ redirectUrl: uri
97
+ }
98
+
99
+ unless user.nil?
100
+ body[:user] = magiclink_user_compose_update_body(**user) unless user.empty?
101
+
102
+ method_str, val = get_login_id_by_method(method:, user:)
103
+ body[method_str.to_sym] = val
104
+ end
105
+
106
+ body
107
+ end
108
+
109
+ # rubocop:disable Metrics/MethodLength
110
+ def magiclink_compose_signin_body(login_id, uri, login_options)
111
+ login_options ||= {}
112
+ unless login_options.is_a?(Hash)
113
+ raise Descope::ArgumentException.new(
114
+ 'Unable to read login_option, not a Hash',
115
+ code: 400
116
+ )
117
+ end
118
+
119
+ body = {
120
+ loginId: login_id,
121
+ redirectUrl: uri,
122
+ loginOptions: {}
123
+ }
124
+
125
+ body[:loginOptions][:stepup] = login_options.fetch(:stepup, false)
126
+ body[:loginOptions][:mfa] = login_options.fetch(:mfa, false)
127
+ body[:loginOptions][:customClaims] = login_options.fetch(:custom_claims, {})
128
+ body[:loginOptions][:ssoAppId] = login_options.fetch(:sso_app_id, nil)
129
+
130
+ body
131
+ end
132
+
133
+ def magiclink_compose_update_user_email_body(login_id, email, uri, add_to_login_ids, on_merge_use_existing)
134
+ {
135
+ loginId: login_id,
136
+ email:,
137
+ redirectUrl: uri,
138
+ addToLoginIDs: add_to_login_ids,
139
+ onMergeUseExisting: on_merge_use_existing
140
+ }
141
+ end
142
+
143
+ def magiclink_compose_update_user_phone_body(login_id, phone, uri, add_to_login_ids, on_merge_use_existing)
144
+ {
145
+ loginId: login_id,
146
+ phone:,
147
+ redirectUrl: uri,
148
+ addToLoginIDs: add_to_login_ids,
149
+ onMergeUseExisting: on_merge_use_existing
150
+ }
151
+ end
152
+
153
+ private
154
+ def magiclink_user_compose_update_body(login_id: nil, name: nil, phone: nil, email: nil, given_name: nil, middle_name: nil, family_name: nil)
155
+ user = {}
156
+ user[:loginId] = login_id if login_id
157
+ user[:name] = name if name
158
+ user[:phone] = phone if phone
159
+ user[:email] = email if email
160
+ user[:givenName] = given_name if given_name
161
+ user[:middleName] = middle_name if middle_name
162
+ user[:familyName] = family_name if family_name
163
+
164
+ user
165
+ end
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+ require 'cgi'
3
+
4
+ module Descope
5
+ module Api
6
+ module V1
7
+ module Auth
8
+ # Holds all the password API calls
9
+ module OAuth
10
+ include Descope::Mixins::Validation
11
+ include Descope::Mixins::Common::EndpointsV1
12
+ include Descope::Mixins::Common::EndpointsV2
13
+
14
+ def oauth_start(provider: nil, return_url: nil, prompt: nil, login_options: nil, refresh_token: nil, template_options: nil)
15
+ body = compose_start_params(login_options:, template_options:)
16
+ url = "#{OAUTH_START_PATH}?provider=#{provider}"
17
+ url += "&redirectUrl=#{CGI.escape(return_url)}" if return_url
18
+ url += "&prompt=#{CGI.escape(prompt)}" if prompt
19
+ post(url, body, {}, refresh_token)
20
+ end
21
+
22
+ def oauth_exchange_token(code = nil)
23
+ exchange_token(OAUTH_EXCHANGE_TOKEN_PATH, code)
24
+ end
25
+
26
+ def oauth_create_redirect_url_for_sign_in_request(stepup: false, custom_claims: {}, mfa: false,
27
+ sso_app_id: nil)
28
+ request_params = {
29
+ stepup:,
30
+ customClaims: custom_claims,
31
+ mfa:,
32
+ ssoAppId: sso_app_id
33
+ }
34
+ post(OAUTH_CREATE_REDIRECT_URL_FOR_SIGN_IN_REQUEST_PATH, request_params)
35
+ end
36
+
37
+ def oauth_create_redirect_url_for_sign_up_request(stepup: false, custom_claims: {}, mfa: false,
38
+ sso_app_id: nil)
39
+ request_params = {
40
+ stepup:,
41
+ customClaims: custom_claims,
42
+ mfa:,
43
+ ssoAppId: sso_app_id
44
+ }
45
+ post(OAUTH_CREATE_REDIRECT_URL_FOR_SIGN_UP_REQUEST_PATH, request_params)
46
+ end
47
+
48
+ private
49
+
50
+ def compose_start_params(login_options: nil, template_options: nil)
51
+ login_options ||= {}
52
+
53
+ unless login_options.is_a?(Hash)
54
+ raise Descope::ArgumentException.new(
55
+ 'Unable to read login_options, not a Hash',
56
+ code: 400
57
+ )
58
+ end
59
+
60
+ body = {}
61
+ body[:stepup] = login_options.fetch(:stepup, false)
62
+ body[:mfa] = login_options.fetch(:mfa, false)
63
+ body[:customClaims] = login_options.fetch(:custom_claims, {})
64
+ body[:ssoAppId] = login_options.fetch(:sso_app_id, nil) if login_options.key?(:sso_app_id)
65
+ body[:templateOptions] = template_options unless template_options.nil?
66
+ body
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,186 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Descope
4
+ module Api
5
+ module V1
6
+ module Auth
7
+ # Holds all the password API calls
8
+ module OTP
9
+ include Descope::Mixins::Validation
10
+ include Descope::Mixins::Common::EndpointsV1
11
+ include Descope::Mixins::Common::EndpointsV2
12
+
13
+ def otp_sign_in(method: nil, login_id: nil, login_options: nil, refresh_token: nil, provider_id: nil,
14
+ template_id: nil, sso_app_id: nil)
15
+ # Sign in (log in) an existing user with the unique login_id you provide. (See 'sign_up' function for an explanation of the
16
+ # login_id field.) Provide the DeliveryMethod required for this user. If the login_id value cannot be used for the
17
+ # DeliverMethod selected (for example, 'login_id = 4567qq445km' and 'DeliveryMethod = email')
18
+ validate_login_id(login_id)
19
+ uri = otp_compose_signin_url(method)
20
+ body = otp_compose_signin_body(login_id, login_options, provider_id, template_id, sso_app_id)
21
+ res = post(uri, body, {}, refresh_token)
22
+ extract_masked_address(res, method)
23
+ end
24
+
25
+ def otp_sign_up(method: nil, login_id: nil, user: {}, provider_id: nil, template_id: nil)
26
+ # Sign up (create) a new user using their email or phone number. Choose a delivery method for OTP
27
+ # verification, for example email, SMS, or WhatsApp.
28
+ # (optional) Include additional user metadata that you wish to preserve.
29
+ user ||= {}
30
+
31
+ raise AuthException unless adjust_and_verify_delivery_method(method, login_id, user)
32
+
33
+ uri = otp_compose_signup_url(method)
34
+ body = otp_compose_signup_body(method, login_id, user, provider_id, template_id)
35
+ res = post(uri, body)
36
+ extract_masked_address(res, method)
37
+ end
38
+
39
+ def otp_sign_up_or_in(method: nil, login_id: nil, login_options: nil, provider_id: nil, template_id: nil,
40
+ sso_app_id: nil)
41
+ # Sign_up_or_in lets you handle both sign up and sign in with a single call. Sign-up_or_in will first
42
+ # determine if login_id is a new or existing end user. If login_id is new, a new end user user will be
43
+ # created and then authenticated using the OTP DeliveryMethod specified.
44
+ # If login_id exists, the end user will be authenticated using the OTP DeliveryMethod specified.
45
+ validate_login_id(login_id)
46
+ uri = otp_compose_sign_up_or_in_url(method)
47
+ body = otp_compose_signin_body(login_id, login_options, provider_id, template_id, sso_app_id)
48
+ res = post(uri, body)
49
+ extract_masked_address(res, method)
50
+ end
51
+
52
+ def otp_verify_code(method: nil, login_id: nil, code: nil)
53
+ validate_login_id(login_id)
54
+ uri = otp_compose_verify_code_url(method)
55
+ request_params = {
56
+ loginId: login_id,
57
+ code:
58
+ }
59
+ res = post(uri, request_params)
60
+ generate_jwt_response(response_body: res, refresh_cookie: res.fetch('refreshJwt', {}))
61
+ end
62
+
63
+ def otp_update_user_email(login_id: nil, email: nil, refresh_token: nil, add_to_login_ids: false,
64
+ on_merge_use_existing: false, provider_id: nil, template_id: nil)
65
+ # Update the email address of an end user, after verifying the authenticity of the end user using OTP.
66
+ validate_login_id(login_id)
67
+ validate_email(email)
68
+ request_params = {
69
+ loginId: login_id,
70
+ email:,
71
+ addToLoginIDs: add_to_login_ids,
72
+ onMergeUseExisting: on_merge_use_existing
73
+ }
74
+ request_params[:providerId] = provider_id if provider_id
75
+ request_params[:templateId] = template_id if template_id
76
+ res = post(UPDATE_USER_EMAIL_OTP_PATH, request_params, {}, refresh_token)
77
+ extract_masked_address(res, DeliveryMethod::EMAIL)
78
+ end
79
+
80
+ def otp_update_user_phone(
81
+ method: nil, login_id: nil, phone: nil, refresh_token: nil, add_to_login_ids: false,
82
+ on_merge_use_existing: false, provider_id: nil, template_id: nil
83
+ )
84
+ # Update the phone number of an existing end user, after verifying the authenticity of the end user using OTP.
85
+ validate_login_id(login_id)
86
+ validate_phone(method, phone)
87
+ uri = otp_compose_update_phone_url(method)
88
+ request_params = {
89
+ loginId: login_id,
90
+ phone:,
91
+ addToLoginIDs: add_to_login_ids,
92
+ onMergeUseExisting: on_merge_use_existing
93
+ }
94
+ request_params[:providerId] = provider_id if provider_id
95
+ request_params[:templateId] = template_id if template_id
96
+ res = post(uri, request_params, {}, refresh_token)
97
+ extract_masked_address(res, method)
98
+ end
99
+
100
+ private
101
+
102
+ def otp_compose_signin_url(method = nil)
103
+ method = Descope::Mixins::Common::DeliveryMethod::EMAIL if method.nil?
104
+ compose_url(SIGN_IN_AUTH_OTP_PATH, method)
105
+ end
106
+
107
+ def otp_compose_signup_url(method = nil)
108
+ method = Descope::Mixins::Common::DeliveryMethod::EMAIL if method.nil?
109
+ compose_url(SIGN_UP_AUTH_OTP_PATH, method)
110
+ end
111
+
112
+ def otp_compose_sign_up_or_in_url(method = nil)
113
+ method = Descope::Mixins::Common::DeliveryMethod::EMAIL if method.nil?
114
+ compose_url(SIGN_UP_OR_IN_AUTH_OTP_PATH, method)
115
+ end
116
+
117
+ def otp_compose_verify_code_url(method = nil)
118
+ method = Descope::Mixins::Common::DeliveryMethod::EMAIL if method.nil?
119
+ compose_url(VERIFY_CODE_AUTH_PATH, method)
120
+ end
121
+
122
+ def otp_compose_update_phone_url(method = nil)
123
+ method = Descope::Mixins::Common::DeliveryMethod::SMS if method.nil?
124
+ compose_url(UPDATE_USER_PHONE_OTP_PATH, method)
125
+ end
126
+
127
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
128
+ def otp_compose_signup_body(method, login_id, user, provider_id, template_id)
129
+ body = {
130
+ loginId: login_id,
131
+ }
132
+
133
+ unless user.nil?
134
+ body[:user] = otp_user_compose_update_body(**user) unless user.empty?
135
+ method_str, val = get_login_id_by_method(method:, user:)
136
+ body[method_str.to_sym] = val
137
+ end
138
+
139
+ body[:provider_id] = provider_id if provider_id
140
+ body[:template_id] = template_id if template_id
141
+
142
+ body
143
+ end
144
+
145
+ def otp_compose_signin_body(login_id, login_options, provider_id, template_id, sso_app_id)
146
+ login_options ||= {}
147
+ unless login_options.is_a?(Hash)
148
+ raise Descope::ArgumentException.new(
149
+ 'Unable to read login_option, not a Hash',
150
+ code: 400
151
+ )
152
+ end
153
+
154
+ body = {
155
+ loginId: login_id,
156
+ loginOptions: {}
157
+ }
158
+ body[:providerId] = provider_id if provider_id
159
+ body[:templateId] = template_id if template_id
160
+ body[:ssoAppId] = sso_app_id if sso_app_id
161
+ body[:loginOptions][:stepup] = login_options.fetch(:stepup, false)
162
+ body[:loginOptions][:mfa] = login_options.fetch(:mfa, false)
163
+ body[:loginOptions][:customClaims] = login_options.fetch(:custom_claims, {})
164
+ body[:loginOptions][:ssoAppId] = login_options.fetch(:sso_app_id, nil)
165
+
166
+ body
167
+ end
168
+
169
+ private
170
+ def otp_user_compose_update_body(login_id: nil, name: nil, phone: nil, email: nil, given_name: nil, middle_name: nil, family_name: nil)
171
+ user = {}
172
+ user[:loginId] = login_id if login_id
173
+ user[:name] = name if name
174
+ user[:phone] = phone if phone
175
+ user[:email] = email if email
176
+ user[:givenName] = given_name if given_name
177
+ user[:middleName] = middle_name if middle_name
178
+ user[:familyName] = family_name if family_name
179
+
180
+ user
181
+ end
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Descope
4
+ module Api
5
+ module V1
6
+ module Auth
7
+ # Holds all the password API calls
8
+ module Password
9
+ include Descope::Mixins::Validation
10
+ include Descope::Mixins::Common::EndpointsV1
11
+ include Descope::Mixins::Common::EndpointsV2
12
+
13
+ def password_sign_up(login_id: nil, password: nil, user: nil)
14
+ # Sign up (create) a new user using a login ID and password.
15
+ # (optional) Include additional user metadata that you wish to save.
16
+ validate_login_id(login_id)
17
+ validate_password(password)
18
+ request_params = {
19
+ loginId: login_id,
20
+ password:
21
+ }
22
+
23
+ request_params[:user] = password_user_compose_update_body(**user) unless user.nil?
24
+ res = post(SIGN_UP_PASSWORD_PATH, request_params)
25
+ generate_jwt_response(response_body: res)
26
+ end
27
+
28
+ def password_sign_in(login_id: nil, password: nil, sso_app_id: nil)
29
+ # Sign-In an existing user utilizing password authentication. This endpoint will return the user's JWT..
30
+ # Return dict in the format
31
+ # {"jwts": [], "user": "", "firstSeen": "", "error": ""}
32
+ # Includes all the jwts tokens (session token, refresh token), token claims, and user information
33
+ validate_login_id(login_id)
34
+ validate_password(password)
35
+ request_params = {
36
+ loginId: login_id,
37
+ password:,
38
+ ssoAppId: sso_app_id
39
+ }
40
+ res = post(SIGN_IN_PASSWORD_PATH, request_params)
41
+ generate_jwt_response(response_body: res)
42
+ end
43
+
44
+ def password_replace(login_id: nil, old_password: nil, new_password: nil)
45
+ # Replace an existing user's password with a new password.
46
+ validate_login_id(login_id)
47
+ validate_password(old_password)
48
+ validate_password(new_password)
49
+ request_params = {
50
+ loginId: login_id,
51
+ oldPassword: old_password,
52
+ newPassword: new_password
53
+ }
54
+ post(REPLACE_PASSWORD_PATH, request_params)
55
+ end
56
+
57
+ def password_update(login_id: nil, new_password: nil, refresh_token: nil)
58
+ # Update an existing user's password with a new password.
59
+ validate_login_id(login_id)
60
+ validate_password(new_password)
61
+ validate_refresh_token_not_nil(refresh_token)
62
+ request_params = {
63
+ loginId: login_id,
64
+ newPassword: new_password
65
+ }
66
+ post(UPDATE_PASSWORD_PATH, request_params, {}, refresh_token)
67
+ end
68
+
69
+ def get_password_policy(refresh_token = nil)
70
+ # Get the configured password policy for the project.
71
+ get(PASSWORD_POLICY_PATH, {}, {}, refresh_token)
72
+ end
73
+
74
+ def password_reset(login_id: nil, redirect_url: nil, provider_id: nil, template_id: nil)
75
+ # Sends a password reset prompt to the user with the given
76
+ # login_id according to the password settings defined in the Descope console.
77
+ # NOTE: The user must be verified according to the configured password reset method.
78
+ validate_login_id(login_id)
79
+ post(SEND_RESET_PASSWORD_PATH,
80
+ loginId: login_id, redirectUrl: redirect_url, providerId: provider_id, templateId: template_id)
81
+ end
82
+
83
+ private
84
+ def password_user_compose_update_body(login_id: nil, name: nil, phone: nil, email: nil, given_name: nil, middle_name: nil, family_name: nil)
85
+ user = {}
86
+ user[:loginId] = login_id if login_id
87
+ user[:name] = name if name
88
+ user[:phone] = phone if phone
89
+ user[:email] = email if email
90
+ user[:givenName] = given_name if given_name
91
+ user[:middleName] = middle_name if middle_name
92
+ user[:familyName] = family_name if family_name
93
+
94
+ user
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end