descope 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
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).