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
data/README.md ADDED
@@ -0,0 +1,1171 @@
1
+ Descope SDK for Ruby
2
+
3
+
4
+ The Descope SDK for Ruby provides convenient access to the Descope user management and authentication API for a backend written in Ruby. You can read more on the Descope Website.
5
+
6
+ # Descope SDK for Ruby
7
+
8
+ The Descope SDK for Ruby provides convenient access to the Descope user management and authentication API
9
+ for a backend written in Ruby. You can read more on the [Descope Website](https://descope.com).
10
+
11
+ ## Requirements
12
+
13
+ The SDK supports Ruby 3.2 and above.
14
+
15
+ ## Installing the SDK
16
+
17
+ Install the package with:
18
+
19
+ ```bash
20
+ gem install descope
21
+ ```
22
+
23
+ ## Setup
24
+
25
+ A Descope `Project ID` is required to initialize the SDK. Find it on the
26
+ [project page in the Descope Console](https://app.descope.com/settings/project).
27
+
28
+ ```ruby
29
+ require 'descope'
30
+
31
+ descope_client = Descope::Client.new(
32
+ {
33
+ project_id: '<project_id>',
34
+ management_key: ENV['MGMT_KEY']
35
+ }
36
+ )
37
+ ```
38
+
39
+ ## Authentication Methods
40
+ These sections show how to use the SDK to perform various authentication/authorization functions:
41
+
42
+ 1. [OTP Authentication](#otp-authentication)
43
+ 2. [Magic Link](#magic-link)
44
+ 3. [Enchanted Link](#enchanted-link)
45
+ 4. [OAuth](#oauth)
46
+ 5. [SSO/SAML](#ssosaml)
47
+ 6. [TOTP Authentication](#totp-authentication)
48
+ 7. [Passwords](#passwords)
49
+ 8. [Session Validation](#session-validation)
50
+ 9. [Roles & Permission Validation](#roles-permission-validation)
51
+ 10. [Tenant selection](#tenant-selection)
52
+ 11. [Logging Out](#logging-out)
53
+
54
+ ## API Management Function
55
+
56
+ These sections show how to use the SDK to perform permission and user management functions. You will need to create an instance of `DescopeClient` by following the [Setup](#setup-1) guide, before you can use any of these methods:
57
+
58
+ 1. [Manage Tenants](#manage-tenants)
59
+ 2. [Manage Users](#manage-users)
60
+ 3. [Manage Access Keys](#manage-access-keys)
61
+ 4. [Manage SSO Setting](#manage-sso-setting)
62
+ 5. [Manage Permissions](#manage-permissions)
63
+ 6. [Manage Roles](#manage-roles)
64
+ 7. [Query SSO Groups](#query-sso-groups)
65
+ 8. [Manage Flows](#manage-flows-and-theme)
66
+ 9. [Manage JWTs](#manage-jwts)
67
+ 10. [Embedded links](#embedded-links)
68
+ 11. [Search Audit](#search-audit)
69
+ 12. [Manage ReBAC Authz](#manage-rebac-authz)
70
+ 13. [Manage Project](#manage-project)
71
+
72
+ If you wish to run any of our code examples and play with them, check out our [Code Examples](#code-examples) section.
73
+
74
+ If you're performing end-to-end testing, check out the [Utils for your end to end (e2e) tests and integration tests](#utils-for-your-end-to-end-e2e-tests-and-integration-tests) section. You will need to use the `DescopeClient` object created under [Setup](#setup-1) guide.
75
+
76
+ For rate limiting information, please confer to the [API Rate Limits](#api-rate-limits) section.
77
+
78
+ ### OTP Authentication
79
+
80
+ Send a user a one-time password (OTP) using your preferred delivery method (_email / SMS_). An email address or phone number must be provided accordingly.
81
+
82
+ The user can either `sign up`, `sign in` or `sign up or in`
83
+
84
+ ```ruby
85
+ # Every user must have a login ID. All other user information is optional
86
+ # For sign up either phone or email is required
87
+ email = 'desmond@descope.com'
88
+ user = {'name': 'Desmond Copeland', 'phone': '212-555-1234', 'email': email}
89
+ masked_address = descope_client.otp_sign_up(method: DeliveryMethod.EMAIL, login_id: 'someone@example.com', user: user)
90
+ ```
91
+
92
+ The user will receive a code using the selected delivery method. Verify that code using:
93
+
94
+ ```ruby
95
+ jwt_response = descope_client.otp_verify_code(
96
+ method: DeliveryMethod.EMAIL, login_id: 'someone@example.com', code: '123456'
97
+ )
98
+ session_token = jwt_response['sessionJwt']
99
+ refresh_token = jwt_response['refreshJwt']
100
+ ```
101
+
102
+ The session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more on [session validation](#session-validation)
103
+
104
+ ### Magic Link
105
+
106
+ Send a user a Magic Link using your preferred delivery method (_email / SMS_).
107
+ The Magic Link will redirect the user to page where the its token needs to be verified.
108
+ This redirection can be configured in code, or generally in the [Descope Console](https://app.descope.com/settings/authentication/magiclink)
109
+
110
+ The user can either `sign up`, `sign in` or `sign up or in`
111
+
112
+ ```ruby
113
+ masked_address = descope_client.magiclink_sign_up_or_in(
114
+ method: DeliveryMethod.EMAIL,
115
+ login_id: 'desmond@descope.com',
116
+ uri: 'https://myapp.com/verify-magic-link', # Set redirect URI here or via console
117
+ )
118
+ ```
119
+
120
+ To verify a magic link, your redirect page must call the validation function on the token (`t`) parameter (`https://your-redirect-address.com/verify?t=<token>`):
121
+
122
+ ```ruby
123
+ jwt_response = descope_client.magiclink_verify_token('token-here')
124
+ session_token = jwt_response['sessionJwt']
125
+ refresh_token = jwt_response['refreshJwt']
126
+ ```
127
+
128
+ The session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more on [session validation](#session-validation)
129
+
130
+ ### Enchanted Link
131
+
132
+ Using the Enchanted Link APIs enables users to sign in by clicking a link
133
+ delivered to their email address. The email will include 3 different links,
134
+ and the user will have to click the right one, based on the 2-digit number that is
135
+ displayed when initiating the authentication process.
136
+
137
+ This method is similar to [Magic Link](#magic-link) but differs in two major ways:
138
+
139
+ - The user must choose the correct link out of the three, instead of having just one
140
+ single link.
141
+ - This supports cross-device clicking, meaning the user can try to log in on one device,
142
+ like a computer, while clicking the link on another device, for instance a mobile phone.
143
+
144
+ The Enchanted Link will redirect the user to page where the its token needs to be verified.
145
+ This redirection can be configured in code per request, or set globally in the [Descope Console](https://app.descope.com/settings/authentication/enchantedlink).
146
+
147
+ The user can either `sign up`, `sign in` or `sign up or in`
148
+
149
+ ```ruby
150
+ res = descope_client.enchanted_link_sign_up_or_in(
151
+ login_id: 'someone@example.com',
152
+ uri: 'https://myapp.com/verify-enchanted-link', # Set redirect URI here or via console
153
+ )
154
+ link_identifier = res['linkId'] # Show the user which link they should press in their email
155
+ pending_ref = res['pendingRef'] # Used to poll for a valid session
156
+ masked_email = res['maskedEmail'] # The email that the message was sent to in a masked format
157
+ ```
158
+
159
+ After sending the link, you must poll to receive a valid session using the `pending_ref` from
160
+ the previous step. A valid session will be returned only after the user clicks the right link.
161
+
162
+ ```ruby
163
+ def poll_for_session(descope_client, pending_ref)
164
+ max_tries = 15
165
+ i = 0
166
+ done = false
167
+ while !done && i < max_tries
168
+ begin
169
+ i += 1
170
+ puts 'waiting 4 seconds for session to be created...'
171
+ sleep(4)
172
+ print '.'
173
+ jwt_response = descope_client.enchanted_link_get_session(pending_ref)
174
+ done = true
175
+ rescue Descope::AuthException, Descope::Unauthorized => e
176
+ puts 'Failed pending session, err: #{e}'
177
+ nil
178
+ end
179
+
180
+ if jwt_response
181
+ puts 'jwt_response: #{jwt_response}'
182
+ refresh_token = jwt_response[Descope::Mixins::Common::REFRESH_SESSION_TOKEN_NAME]['jwt']
183
+
184
+ puts 'refresh_token: #{refresh_token}'
185
+ puts :'Done logging out!'
186
+ descope_client.sign_out(refresh_token)
187
+ puts 'User logged out'
188
+ done = true
189
+ end
190
+ end
191
+ end
192
+
193
+ poll_for_session(descope_client, pending_ref)
194
+ ```
195
+
196
+ To verify an enchanted link, your redirect page must call the validation function on the token (`t`) parameter (`https://your-redirect-address.com/verify?t=<token>`). Once the token is verified, the session polling will receive a valid `jwt_response`.
197
+
198
+ ```ruby
199
+ begin
200
+ descope_client.enchanted_link_verify_token(token=token)
201
+ # Token is valid
202
+ rescue AuthException => e
203
+ # Token is invalid
204
+ end
205
+ ```
206
+
207
+ The session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more on [session validation](#session-validation)
208
+
209
+ ### OAuth
210
+
211
+ Users can authenticate using their social logins, using the OAuth protocol. Configure your OAuth settings on the [Descope console](https://app.descope.com/settings/authentication/social). To start a flow call:
212
+
213
+ ```ruby
214
+
215
+ descope_client.oauth_start(
216
+ provider: 'google', # Choose an oauth provider out of the supported providers
217
+ return_url: 'https://my-app.com/handle-oauth', # Can be configured in the console instead of here
218
+ )
219
+ ```
220
+
221
+ The user will authenticate with the authentication provider, and will be redirected back to the redirect URL, with an appended `code` HTTP URL parameter. Exchange it to validate the user:
222
+
223
+ ```ruby
224
+ jwt_response = descope_client.oauth_exchange_token(code)
225
+ session_token = jwt_response['sessionJwt']
226
+ refresh_token = jwt_response['refreshJwt']
227
+ ```
228
+
229
+ The session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more on [session validation](#session-validation)
230
+
231
+ ### SSO/SAML
232
+
233
+ Users can authenticate to a specific tenant using SAML or Single Sign On. Configure your SSO/SAML settings on the [Descope console](https://app.descope.com/settings/authentication/sso). To start a flow call:
234
+
235
+ ```ruby
236
+
237
+ descope_client.saml_sign_in(
238
+ tenant: 'my-tenant-ID', # Choose which tenant to log into
239
+ return_url: 'https://my-app.com/handle-saml', # Can be configured in the console instead of here
240
+ prompt: 'custom prompt here'
241
+ )
242
+ ```
243
+
244
+ The user will authenticate with the authentication provider configured for that tenant, and will be redirected back to the redirect URL, with an appended `code` HTTP URL parameter. Exchange it to validate the user:
245
+
246
+ ```ruby
247
+ jwt_response = descope_client.saml_exchange_token(code)
248
+ session_token = jwt_response['sessionJwt']
249
+ refresh_token = jwt_response['refreshJwt']
250
+ ```
251
+
252
+ The session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more on [session validation](#session-validation)
253
+
254
+ ### TOTP Authentication
255
+
256
+ The user can authenticate using an authenticator app, such as Google Authenticator.
257
+ Sign up like you would using any other authentication method. The sign up response
258
+ will then contain a QR code `image` that can be displayed to the user to scan using
259
+ their mobile device camera app, or the user can enter the `key` manually or click
260
+ on the link provided by the `provisioning_url`.
261
+
262
+ Existing users can add TOTP using the `update` function.
263
+
264
+ ```ruby
265
+ # Every user must have a login ID. All other user information is optional
266
+ email = 'desmond@descope.com'
267
+ user = {name: 'Desmond Copeland', phone: '212-555-1234', email: 'someone@example.com'}
268
+ totp_response = descope_client.totp_sign_up(method: DeliveryMethod.EMAIL, login_id: 'someone@example.com', user: user)
269
+
270
+ # Use one of the provided options to have the user add their credentials to the authenticator
271
+ provisioning_url = totp_response['provisioningURL']
272
+ image = totp_response['image']
273
+ key = totp_response['key']
274
+ ```
275
+
276
+ There are 3 different ways to allow the user to save their credentials in
277
+ their authenticator app - either by clicking the provisioning URL, scanning the QR
278
+ image or inserting the key manually. After that, signing in is done using the code
279
+ the app produces.
280
+
281
+ ```ruby
282
+ jwt_response = descope_client.totp_sign_in_code(
283
+ login_id: 'someone@example.com',
284
+ code: '123456' # Code from authenticator app
285
+ )
286
+ session_token = jwt_response['sessionJwt']
287
+ refresh_token = jwt_response['refreshJwt']
288
+ ```
289
+
290
+ The session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more on [session validation](#session-validation)
291
+
292
+ ### Passwords
293
+
294
+ The user can also authenticate with a password, though it's recommended to
295
+ prefer passwordless authentication methods if possible. Sign up requires the
296
+ caller to provide a valid password that meets all the requirements configured
297
+ for the [password authentication method](https://app.descope.com/settings/authentication/password) in the Descope console.
298
+
299
+ ```ruby
300
+ # Every user must have a login_id and a password. All other user information is optional
301
+ login_id = 'desmond@descope.com'
302
+ password = 'qYlvi65KaX'
303
+ user = {
304
+ name: 'Desmond Copeland',
305
+ email: login_id,
306
+ }
307
+ jwt_response = descope_client.password_sign_up(login_id:, password:, user:)
308
+ session_token = jwt_response['sessionJwt']
309
+ refresh_token = jwt_response['refreshJwt']
310
+ ```
311
+
312
+ The user can later sign in using the same login_id and password.
313
+
314
+ ```ruby
315
+ jwt_response = descope_client.password_sign_in(login_id:, password:)
316
+ session_token = jwt_response['sessionJwt']
317
+ refresh_token = jwt_response['refreshJwt']
318
+ ```
319
+
320
+ The session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more on [session validation](#session-validation)
321
+
322
+ In case the user needs to update their password, one of two methods are available: Resetting their password or replacing their password
323
+
324
+ **Changing Passwords**
325
+
326
+ _NOTE: send_reset will only work if the user has a validated email address. Otherwise password reset prompts cannot be sent._
327
+
328
+ In the [password authentication method](https://app.descope.com/settings/authentication/password) in the Descope console, it is possible to define which alternative authentication method can be used in order to authenticate the user, in order to reset and update their password.
329
+
330
+ ```ruby
331
+ # Start the reset process by sending a password reset prompt. In this example we'll assume
332
+ # that magic link is configured as the reset method. The optional redirect URL is used in the
333
+ # same way as in regular magic link authentication.
334
+ login_id = 'desmond@descope.com'
335
+ redirect_url = 'https://myapp.com/password-reset'
336
+ descope_client.password_reset(login_id:, redirect_url:)
337
+ ```
338
+
339
+ The magic link, in this case, must then be verified like any other magic link (see the [magic link section](#magic-link) for more details). However, after verifying the user, it is expected
340
+ to allow them to provide a new password instead of the old one. Since the user is now authenticated, this is possible via:
341
+
342
+ ```ruby
343
+ # The refresh token is required to make sure the user is authenticated.
344
+ err = descope_client.password_update(login_id:, new_password: 'xyz123', token: 'token-here')
345
+ ```
346
+
347
+ `update` can always be called when the user is authenticated and has a valid session.
348
+
349
+ Alternatively, it is also possible to replace an existing active password with a new one.
350
+
351
+ ```ruby
352
+ # Replaces the user's current password with a new one
353
+ jwt_response = descope_client.password_replace(login_id: 'login', old_password: '1234', new_password: '4567')
354
+ session_token = jwt_response['sessionJwt']
355
+ refresh_token = jwt_response['refreshJwt']
356
+ ```
357
+
358
+ ### Session Validation
359
+
360
+ Every secure request performed between your client and server needs to be validated. The client sends
361
+ the session and refresh tokens with every request, and they are validated using one of the following:
362
+
363
+ ```ruby
364
+ # Validate the session. Will raise if expired
365
+ begin
366
+ jwt_response = descope_client.validate_session('session_token')
367
+ rescue AuthException => e
368
+ # Session expired
369
+ end
370
+
371
+ # If validate_session raises an exception, you will need to refresh the session using
372
+ jwt_response = descope_client.refresh_session('refresh_token')
373
+
374
+ # Alternatively, you could combine the two and
375
+ # have the session validated and automatically refreshed when expired
376
+ jwt_response = descope_client.validate_and_refresh_session('session_token', 'refresh_token')
377
+ ```
378
+
379
+ Choose the right session validation and refresh combination that suits your needs.
380
+
381
+ Note: all those validation apis can receive an optional 'audience' parameter that should be provided when using jwt that has the 'aud' claim)
382
+
383
+ Refreshed sessions return the same response as is returned when users first sign up / log in,
384
+ containing the session and refresh tokens, as well as all of the JWT claims.
385
+ Make sure to return the tokens from the response to the client, or updated the cookie if you're using it.
386
+
387
+ Usually, the tokens can be passed in and out via HTTP headers or via a cookie.
388
+ The implementation can defer according to your framework of choice. See our [samples](#code-samples) for a few examples.
389
+
390
+ If Roles & Permissions are used, validate them immediately after validating the session. See the [next section](#roles--permission-validation)
391
+ for more information.
392
+
393
+ ### Roles & Permission Validation
394
+
395
+ When using Roles & Permission, it's important to validate the user has the required
396
+ authorization immediately after making sure the session is valid. Taking the `jwt_response`
397
+ received by the [session validation](#session-validation), call the following functions:
398
+
399
+ For multi-tenant uses:
400
+
401
+ ```ruby
402
+ # You can validate specific permissions
403
+ valid_permissions = descope_client.validate_tenant_permissions(
404
+ jwt_response: 'resp', tenant: 'my-tenant-ID', permissions: ['Permission to validate']
405
+ )
406
+
407
+ unless valid_permissions
408
+ # Deny access
409
+ end
410
+
411
+ # Or validate roles directly
412
+ valid_roles = descope_client.validate_tenant_roles(
413
+ jwt_response: 'resp', tenant: 'my-tenant-ID', roles: ['Role to validate']
414
+ )
415
+
416
+ unless valid_roles
417
+ # Deny access
418
+ end
419
+ ```
420
+
421
+ When not using tenants use:
422
+
423
+ ```ruby
424
+ # You can validate specific permissions
425
+ valid_permissions = descope_client.validate_permissions(
426
+ jwt_response: 'resp', permissions: ['Permission to validate']
427
+ )
428
+ unless valid_permissions
429
+ # Deny access
430
+ end
431
+
432
+ # Or validate roles directly
433
+ valid_roles = descope_client.validate_roles(
434
+ jwt_response: 'resp', roles: ['Role to validate']
435
+ )
436
+
437
+ unless valid_roles
438
+ # Deny access
439
+ end
440
+ ```
441
+
442
+ ### Tenant selection
443
+ For a user that has permissions to multiple tenants, you can set a specific tenant as the current selected one
444
+ This will add an extra attribute to the refresh JWT and the session JWT with the selected tenant ID
445
+
446
+ ```ruby
447
+ tenant_id = 't1'
448
+ jwt_response = descope_client.select_tenant(tenant_id:, refresh_token: 'refresh_token')
449
+ ```
450
+
451
+ ### Signing Out
452
+
453
+ You can log out a user from an active session by providing their `refresh_token` for that session.
454
+ After calling this function, you must invalidate or remove any cookies you have created.
455
+
456
+ ```ruby
457
+ descope_client.sign_out('refresh_token')
458
+ ```
459
+
460
+ It is also possible to sign the user out of all the devices they are currently signed-in with. Calling `logout_all` will
461
+ invalidate all user's refresh tokens. After calling this function, you must invalidate or remove any cookies you have created.
462
+
463
+ ```ruby
464
+ descope_client.sign_out_all('refresh_token')
465
+ ```
466
+
467
+ ## Management API
468
+
469
+ It is very common for some form of management or automation to be required. These can be performed
470
+ using the management API. Please note that these actions are more sensitive as they are administrative
471
+ in nature. Please use responsibly.
472
+
473
+ ### Setup
474
+
475
+ To use the management API you'll need a `Management Key` along with your `Project ID`.
476
+ Create one in the [Descope Console](https://app.descope.com/settings/company/managementkeys).
477
+
478
+ ```ruby
479
+ require 'descope'
480
+
481
+ # Initialized after setting the DESCOPE_PROJECT_ID and the DESCOPE_MANAGEMENT_KEY env vars
482
+ project_id = '<project_id>'
483
+ client = Descope::Client.new(
484
+ {
485
+ project_id: project_id,
486
+ management_key: ENV['MGMT_KEY']
487
+ }
488
+ )
489
+
490
+ ```
491
+
492
+ ### Manage Tenants
493
+
494
+ You can create, update, delete or load tenants:
495
+
496
+ ```ruby
497
+ # You can optionally set your own ID when creating a tenant
498
+ descope_client.create_tenant(
499
+ name: 'My First Tenant',
500
+ id: 'my-custom-id', # This is optional.
501
+ self_provisioning_domains: ['domain.com'],
502
+ custom_attributes: { 'attribute-name': 'value' },
503
+ )
504
+
505
+ # Update will override all fields as is. Use carefully.
506
+ descope_client.update_tenant(
507
+ id: 'my-custom-id',
508
+ name: 'My First Tenant',
509
+ self_provisioning_domains: %w[domain.com another-domain.com],
510
+ custom_attributes: { 'attribute-name': 'value' },
511
+ )
512
+
513
+ # Tenant deletion cannot be undone. Use carefully.
514
+ descope_client.delete_tenant('my-custom-id')
515
+
516
+ # Load tenant by id
517
+ tenant_resp = descope_client.load_tenant('my-custom-id')
518
+
519
+ # Load all tenants
520
+ tenants_resp = descope_client.load_all_tenants
521
+ tenants = tenants_resp['tenants']
522
+ tenants.each do |tenant|
523
+ # Do something
524
+ end
525
+
526
+ # search all tenants
527
+ tenants_resp = descope_client.search_all_tenants(ids: ['id1'], names: ['name1'], custom_attributes: { 'k1': 'v1' }, self_provisioning_domains: ['spd1'])
528
+ tenants = tenants_resp['tenants']
529
+ tenants.each do |tenant|
530
+ # Do something
531
+ tenant_id = tenant['id']
532
+ end
533
+ ```
534
+
535
+ ### Manage Users
536
+
537
+ You can create, update, delete or load users, as well as setting new password, expire password and search according to filters:
538
+
539
+ ```ruby
540
+ # A user must have a login ID, other fields are optional.
541
+ # Roles should be set directly if no tenants exist, otherwise set
542
+ # on a per-tenant basis.
543
+ descope_client.create_user(
544
+ login_id: 'desmond@descope.com',
545
+ email: 'desmond@descope.com',
546
+ display_name: 'Desmond Copeland',
547
+ user_tenants: [
548
+ AssociatedTenant('my-tenant-id', ['role-name1']),
549
+ ],
550
+ )
551
+
552
+ # Alternatively, a user can be created and invited via an email message.
553
+ # Make sure to configure the invite URL in the Descope console prior to using this function,
554
+ # and that an email address is provided in the information.
555
+ associated_tenants = [{ tenant_id: 'tenant_id1', role_names: %w[role_name1 role_name2] }]
556
+ descope_client.invite_user(
557
+ login_id: 'desmond@descope.com',
558
+ email: 'desmond@descope.com',
559
+ display_name: 'Desmond Copeland',
560
+ user_tenants: client.associated_tenants_to_hash_array(associated_tenants),
561
+ )
562
+
563
+ # Update will override all fields as is. Use carefully.
564
+ descope_client.update_user(
565
+ login_id: 'desmond@descope.com',
566
+ email: 'desmond@descope.com',
567
+ display_name: 'Desmond Copeland',
568
+ user_tenants: client.associated_tenants_to_hash_array(associated_tenants)
569
+ )
570
+
571
+ # Update explicit data for a user rather than overriding all fields
572
+ descope_client.update_login_id(
573
+ login_id: 'desmond@descope.com',
574
+ new_login_id: 'bane@descope.com'
575
+ )
576
+
577
+ descope_client.update_phone(
578
+ login_id: 'desmond@descope.com',
579
+ phone: '+18005551234',
580
+ verified: true
581
+ )
582
+
583
+ descope_client.user_remove_tenant_roles(
584
+ login_id: 'desmond@descope.com',
585
+ tenant_id: 'my-tenant-id',
586
+ role_names: ['role-name1']
587
+ )
588
+
589
+ # User deletion cannot be undone. Use carefully.
590
+ descope_client.delete_user('desmond@descope.com')
591
+
592
+ # Load specific user
593
+ user_resp = descope_client.load_user('desmond@descope.com')
594
+ user = user_resp['user']
595
+
596
+ # If needed, users can be loaded using the user ID as well
597
+ user_resp = descope_client.load_by_user_id('<user-id>')
598
+ user = user_resp['user']
599
+
600
+ # Logout user from all devices by login ID
601
+ descope_client.logout_user('<login-id>')
602
+
603
+ # Logout user from all devices by user ID
604
+ descope_client.logout_user_by_id('<user-id>')
605
+
606
+ # Search all users, optionally according to tenant and/or role filter
607
+ # results can be paginated using the limit and page parameters
608
+ users_resp = descope_client.search_all_users(tenant_ids = ['my-tenant-id'])
609
+ users = users_resp['users']
610
+ users.each do |user|
611
+ # Do something
612
+ end
613
+ ```
614
+
615
+ #### Set or Expire User Password
616
+
617
+ You can set or expire a user's password.
618
+ Note: When setting a password, it will automatically be set as expired.
619
+ The user will not be able log-in using an expired password, and will be required replace it on next login.
620
+
621
+ ```ruby
622
+ # Set a user's password
623
+ descope_client.set_password(login_id: '<login-id>', password: '<some-password>');
624
+
625
+ # Or alternatively, expire a user password
626
+ descope_client.expire_password('<login-id>')
627
+ ```
628
+
629
+ ### Manage Access Keys
630
+
631
+ You can create, update, delete or load access keys, as well as search according to filters:
632
+
633
+ ```ruby
634
+ # An access key must have a name and expiration, other fields are optional.
635
+ # Roles should be set directly if no tenants exist, otherwise set
636
+ # on a per-tenant basis.
637
+ associated_tenants = [{ tenant_id: 'tenant_id1', role_names: %w[role_name1 role_name2] }]
638
+ create_resp = descope_client.create_access_key(
639
+ name: 'name',
640
+ expire_time: 1677844931,
641
+ key_tenants: associated_tenants
642
+ )
643
+ key = create_resp['key']
644
+ cleartext = create_resp['cleartext'] # make sure to save the returned cleartext securely. It will not be returned again.
645
+
646
+ # Load a specific access key
647
+ access_key_resp = descope_client.load_access_key('key-id')
648
+ access_key = access_key_resp['key']
649
+
650
+ # Search all access keys, optionally according to a tenant filter
651
+ keys_resp = descope_client.search_all_access_keys(['my-tenant-id'])
652
+ keys = keys_resp['keys']
653
+ keys.each do |key|
654
+ # Do something with key
655
+ end
656
+
657
+ # Update will rename the access key
658
+ descope_client.update_access_key(
659
+ id: 'key-id',
660
+ name: 'new name'
661
+ )
662
+
663
+ # Access keys can be deactivated to prevent usage. This can be undone using 'activate'.
664
+ descope_client.deactivate_access_key('key-id')
665
+
666
+ # Disabled access keys can be activated once again.
667
+ descope_client.activate_access_key('key-id')
668
+
669
+ # Access key deletion cannot be undone. Use carefully.
670
+ descope_client.delete_access_key('key-id')
671
+
672
+ ```
673
+
674
+ ### Manage SSO SAML Settings
675
+
676
+ You can manage SSO settings and map SSO group roles and user attributes.
677
+
678
+ ```ruby
679
+ # You can get SSO SAML settings for a tenant
680
+ sso_settings_res = descope_client.sso_get_settings('tenant-id')
681
+
682
+ # You can configure SSO SAML settings manually by setting the required fields directly
683
+ descope_client.configure_sso_saml_metadata(
684
+ tenant_id: '123', # Which tenant this configuration is for
685
+ settings: {
686
+ name: 'test',
687
+ clientId: 'test',
688
+ scope: ['test'],
689
+ userAttrMapping: {
690
+ loginId: 'test',
691
+ username: 'test',
692
+ name: 'test'
693
+ },
694
+ callbackDomain: 'test'
695
+ },
696
+ redirect_url: 'https://your.domain.com', # Global redirection after successful authentication
697
+ domain: 'tenant-users.com' # Users authentication with this domain will be logged in to this tenant
698
+ )
699
+ ```
700
+
701
+
702
+ ### Manage Permissions
703
+
704
+ You can create, update, delete or load permissions:
705
+
706
+ ```ruby
707
+ # You can optionally set a description for a permission.
708
+ descope_client.create_permission(
709
+ name:'My Permission',
710
+ description:'Optional description to briefly explain what this permission allows.'
711
+ )
712
+
713
+ # Update will override all fields as is. Use carefully.
714
+ descope_client.mgmt.update_permission(
715
+ name: 'My Permission',
716
+ new_name: 'My Updated Permission',
717
+ description: 'A revised description'
718
+ )
719
+
720
+ # Permission deletion cannot be undone. Use carefully.
721
+ descope_client.mgmt.permission.delete('My Updated Permission')
722
+
723
+ # Load all permissions
724
+ permissions_resp = descope_client.load_all_permissions
725
+ permissions = permissions_resp['permissions']
726
+ permissions.each do |permission|
727
+ # Do something
728
+ end
729
+ ```
730
+
731
+ ### Manage Roles
732
+
733
+ You can create, update, delete or load roles:
734
+
735
+ ```ruby
736
+ # You can optionally set a description and associated permission for a roles.
737
+ descope_client.create_role(
738
+ name: 'My Role',
739
+ description: 'Optional description to briefly explain what this role allows.',
740
+ permission_names: ['My Updated Permission'],
741
+ )
742
+
743
+ # Update will override all fields as is. Use carefully.
744
+ descope_client.update_role(
745
+ name: 'My Role',
746
+ new_name: 'My Updated Role',
747
+ description: 'A revised description',
748
+ permission_names: ['My Updated Permission', 'Another Permission']
749
+ )
750
+
751
+ # Role deletion cannot be undone. Use carefully.
752
+ descope_client.delete_role('My Updated Role')
753
+
754
+ # Load all roles
755
+ roles_resp = descope_client.load_all_roles()
756
+ roles = roles_resp['roles']
757
+ roles.each do |role|
758
+ # Do something
759
+ end
760
+ #
761
+ ```
762
+
763
+ ### Manage Flows and Theme
764
+
765
+ You can list your flows and also import and export flows and screens, or the project theme:
766
+
767
+ ```ruby
768
+ # List all project flows
769
+ flows_resp = descope_client.list_or_search_flows()
770
+ puts("Total number of flows: #{flows_resp['total']}")
771
+ flows = flows_resp['flows']
772
+ flows.each do |flow|
773
+ # Do something
774
+ end
775
+
776
+ # Export a selected flow by id for the flow and matching screens.
777
+ exported_flow_and_screens = descope_client.export_flow('sign-up-or-in')
778
+
779
+ # Import a given flow and screens to the flow matching the id provided.
780
+ imported_flow_and_screens = descope_client.import_flow(
781
+ flow_id: 'sign-up-or-in',
782
+ flow: {},
783
+ screens: []
784
+ )
785
+
786
+ # Export your project theme.
787
+ exported_theme = descope_client.export_theme
788
+
789
+ # Import a theme to your project.
790
+ imported_theme = descope_client.import_theme('theme')
791
+
792
+ ```
793
+
794
+ ### Query SCIM Groups
795
+
796
+ You can query SCIM groups:
797
+
798
+ ```ruby
799
+ # Load all groups for a given tenant id
800
+ groups_resp = descope_client.scim_search_groups(
801
+ group_id: 'group_id',
802
+ display_name: 'display_name',
803
+ members: ['members'],
804
+ external_id: 'external_id',
805
+ excluded_attributes: { abc: '123' }
806
+ )
807
+
808
+ # Load SCIM group
809
+ group = descope_client.scim_load_group(
810
+ tenant_id: 'tenant-id',
811
+ group_id: 'group-id'
812
+ )
813
+
814
+ # Load SCIM group members
815
+ group = descope_client.scim_create_group(
816
+ group_id: 'group_id',
817
+ display_name: 'display_name',
818
+ members: ['members'],
819
+ external_id: 'external_id',
820
+ excluded_attributes: { abc: '123' }
821
+ )
822
+ ```
823
+
824
+ ### Manage JWTs
825
+
826
+ You can add custom claims to a valid JWT.
827
+
828
+ ```ruby
829
+ updated_jwt = descope_client.update_jwt(
830
+ jwt: 'original-jwt',
831
+ custom_claims: {
832
+ 'custom-key1': 'custom-value1',
833
+ 'custom-key2': 'custom-value2'
834
+ },
835
+ )
836
+ ```
837
+
838
+ # Note 1: The generate code/link functions, work only for test users, will not work for regular users.
839
+
840
+ # Note 2: In case of testing sign-in / sign-up operations with test users, need to make sure to generate the code prior calling the sign-in / sign-up operations.
841
+
842
+ ### Embedded links
843
+
844
+ Embedded links can be created to directly receive a verifiable token without sending it.
845
+
846
+ This token can then be verified using the magic link 'verify' function, either directly or through a flow.
847
+
848
+ ```ruby
849
+ token = descope_client.generate_embedded_link(login_id: 'desmond@descope.com', custom_claims: {'key1':'value1'})
850
+ ```
851
+
852
+ ### Search Audit
853
+
854
+ You can perform an audit search for either specific values or full-text across the fields. Audit search is limited to the last 30 days.
855
+ Below are some examples. For a full list of available search criteria options, see the function documentation.
856
+
857
+ ```ruby
858
+ # Full text search on last 10 days
859
+ audits = descope_client.audit_search(
860
+ no_tenants: true,
861
+ actions: ['LoginSucceed'],
862
+ user_ids: %w[user1 user2],
863
+ exclude_actions: %w[exclude1 exclude2],
864
+ devices: %w[Bot Mobile Desktop Tablet Unknown],
865
+ methods: %w[otp totp magiclink oauth saml password],
866
+ geos: %w[US IL],
867
+ remote_addresses: %w[remote1 remote2],
868
+ login_ids: %w[login1 login2],
869
+ tenants: %w[tenant1 tenant2],
870
+ text: 'text123',
871
+ from_ts: time.now - 10 * 24 * 60 * 60,
872
+ to_ts: time.now - 1 * 24 * 60 * 60,
873
+ )
874
+
875
+ # Search successful logins in the last 30 days
876
+ audits = descope_client.audit_search(actions: ['LoginSucceed'])
877
+ ```
878
+
879
+ ### Manage ReBAC Authz
880
+
881
+ Descope supports full relation based access control (ReBAC) using a [Google Zanzibar](https://research.google/pubs/pub48190/) like schema and operations.
882
+ A schema comprises namespaces (entities like documents, folders, orgs, etc.) and each namespace has relation definitions to define relations.
883
+ Each relation definition can be simple (either you have it or not) or complex (union of nodes).
884
+
885
+ A simple example for a file system like schema would be:
886
+
887
+ ```yaml
888
+ # Example schema for the authz tests
889
+ name: Files
890
+ namespaces:
891
+ - name: org
892
+ relationDefinitions:
893
+ - name: parent
894
+ - name: member
895
+ complexDefinition:
896
+ nType: union
897
+ children:
898
+ - nType: child
899
+ expression:
900
+ neType: self
901
+ - nType: child
902
+ expression:
903
+ neType: relationLeft
904
+ relationDefinition: parent
905
+ relationDefinitionNamespace: org
906
+ targetRelationDefinition: member
907
+ targetRelationDefinitionNamespace: org
908
+ - name: folder
909
+ relationDefinitions:
910
+ - name: parent
911
+ - name: owner
912
+ complexDefinition:
913
+ nType: union
914
+ children:
915
+ - nType: child
916
+ expression:
917
+ neType: self
918
+ - nType: child
919
+ expression:
920
+ neType: relationRight
921
+ relationDefinition: parent
922
+ relationDefinitionNamespace: folder
923
+ targetRelationDefinition: owner
924
+ targetRelationDefinitionNamespace: folder
925
+ - name: editor
926
+ complexDefinition:
927
+ nType: union
928
+ children:
929
+ - nType: child
930
+ expression:
931
+ neType: self
932
+ - nType: child
933
+ expression:
934
+ neType: relationRight
935
+ relationDefinition: parent
936
+ relationDefinitionNamespace: folder
937
+ targetRelationDefinition: editor
938
+ targetRelationDefinitionNamespace: folder
939
+ - nType: child
940
+ expression:
941
+ neType: targetSet
942
+ targetRelationDefinition: owner
943
+ targetRelationDefinitionNamespace: folder
944
+ - name: viewer
945
+ complexDefinition:
946
+ nType: union
947
+ children:
948
+ - nType: child
949
+ expression:
950
+ neType: self
951
+ - nType: child
952
+ expression:
953
+ neType: relationRight
954
+ relationDefinition: parent
955
+ relationDefinitionNamespace: folder
956
+ targetRelationDefinition: viewer
957
+ targetRelationDefinitionNamespace: folder
958
+ - nType: child
959
+ expression:
960
+ neType: targetSet
961
+ targetRelationDefinition: editor
962
+ targetRelationDefinitionNamespace: folder
963
+ - name: doc
964
+ relationDefinitions:
965
+ - name: parent
966
+ - name: owner
967
+ complexDefinition:
968
+ nType: union
969
+ children:
970
+ - nType: child
971
+ expression:
972
+ neType: self
973
+ - nType: child
974
+ expression:
975
+ neType: relationRight
976
+ relationDefinition: parent
977
+ relationDefinitionNamespace: doc
978
+ targetRelationDefinition: owner
979
+ targetRelationDefinitionNamespace: folder
980
+ - name: editor
981
+ complexDefinition:
982
+ nType: union
983
+ children:
984
+ - nType: child
985
+ expression:
986
+ neType: self
987
+ - nType: child
988
+ expression:
989
+ neType: relationRight
990
+ relationDefinition: parent
991
+ relationDefinitionNamespace: doc
992
+ targetRelationDefinition: editor
993
+ targetRelationDefinitionNamespace: folder
994
+ - nType: child
995
+ expression:
996
+ neType: targetSet
997
+ targetRelationDefinition: owner
998
+ targetRelationDefinitionNamespace: doc
999
+ - name: viewer
1000
+ complexDefinition:
1001
+ nType: union
1002
+ children:
1003
+ - nType: child
1004
+ expression:
1005
+ neType: self
1006
+ - nType: child
1007
+ expression:
1008
+ neType: relationRight
1009
+ relationDefinition: parent
1010
+ relationDefinitionNamespace: doc
1011
+ targetRelationDefinition: viewer
1012
+ targetRelationDefinitionNamespace: folder
1013
+ - nType: child
1014
+ expression:
1015
+ neType: targetSet
1016
+ targetRelationDefinition: editor
1017
+ targetRelationDefinitionNamespace: doc
1018
+ ```
1019
+
1020
+ Descope SDK allows you to fully manage the schema and relations as well as perform simple (and not so simple) checks regarding the existence of relations.
1021
+
1022
+ ```ruby
1023
+ # Load the existing schema
1024
+ schema = descope_client.authz_load_schema
1025
+
1026
+ # Save schema and make sure to remove all namespaces not listed
1027
+ descope_client.authz_save_schema(schema: schema, upgrade: true)
1028
+
1029
+ # Create a relation between a resource and user
1030
+ descope_client.authz_create_relations(
1031
+ [
1032
+ {
1033
+ resource: 'some-doc',
1034
+ relationDefinition: 'owner',
1035
+ namespace: 'doc',
1036
+ target: 'u1'
1037
+ }
1038
+ ]
1039
+ )
1040
+
1041
+ # Check if target has the relevant relation
1042
+ # The answer should be true because an owner is also a viewer
1043
+ relations = descope_client.authz_has_relations?(
1044
+ [
1045
+ {
1046
+ resource: 'some-doc',
1047
+ relationDefinition: 'viewer',
1048
+ namespace: 'doc',
1049
+ target: 'u1'
1050
+ }
1051
+ ]
1052
+ )
1053
+ ```
1054
+
1055
+ ### Manage Project
1056
+
1057
+ You can change the project name, as well as to clone the current project to a new one.
1058
+
1059
+ ```ruby
1060
+
1061
+ # Change the project name
1062
+ descope.client.rename_project('new-project-name')
1063
+
1064
+ # Clone the current project, including its settings and configurations.
1065
+ # Note that this action is supported only with a pro license or above.
1066
+ # Users, tenants and access keys are not cloned.
1067
+ clone_resp = descope.client.clone_project('new-project-name')
1068
+ ```
1069
+
1070
+ ### Utils for your end to end (e2e) tests and integration tests
1071
+
1072
+ To ease your e2e tests, we exposed dedicated management methods,
1073
+ that way, you don't need to use 3rd party messaging services in order to receive sign-in/up Emails or SMS, and avoid the need of parsing the code and token from them.
1074
+
1075
+ ```ruby
1076
+ # User for test can be created, this user will be able to generate code/link without
1077
+ # the need of 3rd party messaging services.
1078
+ # Test user must have a loginId, other fields are optional.
1079
+ # Roles should be set directly if no tenants exist, otherwise set
1080
+ # on a per-tenant basis.
1081
+
1082
+ associated_tenants = [{ tenant_id: 'tenant_id1', role_names: %w[role_name1 role_name2] }]
1083
+ descope_client.create_test_user(
1084
+ login_id: 'desmond@descope.com',
1085
+ email: 'desmond@descope.com',
1086
+ display_name: 'Desmond Copeland',
1087
+ user_tenants: client.associated_tenants_to_hash_array(associated_tenants)
1088
+ )
1089
+
1090
+ # Now test user got created, and this user will be available until you delete it,
1091
+ # you can use any management operation for test user CRUD.
1092
+ # You can also delete all test users.
1093
+ descope_client.delete_all_test_users
1094
+
1095
+ # OTP code can be generated for test user, for example:
1096
+ resp = descope_client.generate_otp_for_test_user(
1097
+ method: DeliveryMethod.EMAIL, login_id: 'login-id'
1098
+ )
1099
+ code = resp['code']
1100
+ # Now you can verify the code is valid (using descope_client.*.verify for example)
1101
+
1102
+ # Same as OTP, magic link can be generated for test user, for example:
1103
+ resp = descope_client.generate_magic_link_for_test_user(
1104
+ method: DeliveryMethod.EMAIL,
1105
+ login_id: 'login-id',
1106
+ )
1107
+ link = resp['link']
1108
+
1109
+ # Enchanted link can be generated for test user, for example:
1110
+ resp = descope_client.generate_enchanted_link_for_test_user(
1111
+ 'login-id', ''
1112
+ )
1113
+ link = resp['link']
1114
+ pending_ref = resp['pendingRef']
1115
+ ```
1116
+
1117
+ ## API Rate Limits
1118
+
1119
+ Handle API rate limits by comparing the exception to the APIRateLimitExceeded exception, which includes the RateLimitParameters map with the key 'Retry-After.' This key indicates how many seconds until the next valid API call can take place.
1120
+
1121
+ ```ruby
1122
+ begin
1123
+ descope_client.magiclink_sign_up_or_in(
1124
+ method: DeliveryMethod.EMAIL,
1125
+ login_id: 'desmond@descope.com',
1126
+ uri: 'https://myapp.com/verify-magic-link',
1127
+ )
1128
+ rescue Descope::RateLimitException => e
1129
+ retry_after_seconds = e['API_RATE_LIMIT_RETRY_AFTER_HEADER']
1130
+ puts "Rate limit exceeded, retry after #{retry_after_seconds} seconds"
1131
+ end
1132
+ # This variable indicates how many seconds until the next valid API call can take place.
1133
+ ```
1134
+
1135
+ ## Code Samples
1136
+
1137
+ You can find various usage samples in the [samples folder](https://github.com/descope/ruby-sdk/blob/main/samples).
1138
+
1139
+ ## Run Locally
1140
+
1141
+ ### Prerequisites
1142
+
1143
+ - Ruby 3.3.0 or higher
1144
+ - Bundler
1145
+
1146
+
1147
+ ### Install dependencies
1148
+
1149
+ ```bash
1150
+ bundle install
1151
+ ```
1152
+
1153
+ ### Run tests
1154
+
1155
+ Running all tests:
1156
+
1157
+ ```bash
1158
+ bundle exec rspec
1159
+ ```
1160
+
1161
+ ## Learn More
1162
+
1163
+ To learn more please see the [Descope Documentation and API reference page](https://docs.descope.com/).
1164
+
1165
+ ## Contact Us
1166
+
1167
+ If you need help you can email [Descope Support](mailto:support@descope.com)
1168
+
1169
+ ## License
1170
+
1171
+ The Descope SDK for Python is licensed for use under the terms and conditions of the [MIT license Agreement](https://github.com/descope/ruby-sdk/blob/main/LICENSE).