descope 1.0.4 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yaml +15 -27
  3. data/.github/workflows/publish-gem.yaml +36 -0
  4. data/Gemfile +12 -14
  5. data/Gemfile.lock +40 -87
  6. data/README.md +39 -17
  7. data/examples/ruby/access_key_app.rb +4 -2
  8. data/examples/ruby/management/Gemfile.lock +2 -2
  9. data/examples/ruby/management/role_app.rb +8 -3
  10. data/examples/ruby-on-rails-api/descope/.gitignore +58 -28
  11. data/examples/ruby-on-rails-api/descope/Gemfile.lock +3 -3
  12. data/examples/ruby-on-rails-api/descope/app/assets/builds/App.css +62 -0
  13. data/examples/ruby-on-rails-api/descope/app/assets/builds/App.css.map +7 -0
  14. data/examples/ruby-on-rails-api/descope/app/assets/builds/application.css +62 -0
  15. data/examples/ruby-on-rails-api/descope/app/assets/builds/application.css.map +7 -0
  16. data/examples/ruby-on-rails-api/descope/app/assets/builds/application.js +40369 -0
  17. data/examples/ruby-on-rails-api/descope/app/assets/builds/application.js.map +7 -0
  18. data/examples/ruby-on-rails-api/descope/app/assets/builds/components/App.css +62 -0
  19. data/examples/ruby-on-rails-api/descope/app/assets/builds/components/App.css.map +7 -0
  20. data/examples/ruby-on-rails-api/descope/app/assets/builds/components/App.js +27979 -0
  21. data/examples/ruby-on-rails-api/descope/app/assets/builds/components/App.js.map +7 -0
  22. data/examples/ruby-on-rails-api/descope/app/assets/builds/components/Dashboard.css +62 -0
  23. data/examples/ruby-on-rails-api/descope/app/assets/builds/components/Dashboard.css.map +7 -0
  24. data/examples/ruby-on-rails-api/descope/app/assets/builds/components/Dashboard.js +27118 -0
  25. data/examples/ruby-on-rails-api/descope/app/assets/builds/components/Dashboard.js.map +7 -0
  26. data/examples/ruby-on-rails-api/descope/app/assets/builds/components/Home.css +62 -0
  27. data/examples/ruby-on-rails-api/descope/app/assets/builds/components/Home.css.map +7 -0
  28. data/examples/ruby-on-rails-api/descope/app/assets/builds/components/Home.js +27113 -0
  29. data/examples/ruby-on-rails-api/descope/app/assets/builds/components/Home.js.map +7 -0
  30. data/examples/ruby-on-rails-api/descope/app/assets/builds/components/Login.css +62 -0
  31. data/examples/ruby-on-rails-api/descope/app/assets/builds/components/Login.css.map +7 -0
  32. data/examples/ruby-on-rails-api/descope/app/assets/builds/components/Login.js +27131 -0
  33. data/examples/ruby-on-rails-api/descope/app/assets/builds/components/Login.js.map +7 -0
  34. data/examples/ruby-on-rails-api/descope/app/assets/builds/components/Profile.css +62 -0
  35. data/examples/ruby-on-rails-api/descope/app/assets/builds/components/Profile.css.map +7 -0
  36. data/examples/ruby-on-rails-api/descope/app/assets/builds/components/Profile.js +27168 -0
  37. data/examples/ruby-on-rails-api/descope/app/assets/builds/components/Profile.js.map +7 -0
  38. data/examples/ruby-on-rails-api/descope/app/assets/builds/components/index.css +62 -0
  39. data/examples/ruby-on-rails-api/descope/app/assets/builds/components/index.css.map +7 -0
  40. data/examples/ruby-on-rails-api/descope/app/assets/builds/components/index.js +28250 -0
  41. data/examples/ruby-on-rails-api/descope/app/assets/builds/components/index.js.map +7 -0
  42. data/examples/ruby-on-rails-api/descope/app/assets/builds/controllers/application.js +2456 -0
  43. data/examples/ruby-on-rails-api/descope/app/assets/builds/controllers/application.js.map +7 -0
  44. data/examples/ruby-on-rails-api/descope/app/assets/builds/controllers/index.js +2453 -0
  45. data/examples/ruby-on-rails-api/descope/app/assets/builds/controllers/index.js.map +7 -0
  46. data/examples/ruby-on-rails-api/descope/app/assets/builds/reportWebVitals.js +211 -0
  47. data/examples/ruby-on-rails-api/descope/app/assets/builds/reportWebVitals.js.map +7 -0
  48. data/examples/ruby-on-rails-api/descope/app/assets/builds/routes/index.css +62 -0
  49. data/examples/ruby-on-rails-api/descope/app/assets/builds/routes/index.css.map +7 -0
  50. data/examples/ruby-on-rails-api/descope/app/assets/builds/routes/index.js +27973 -0
  51. data/examples/ruby-on-rails-api/descope/app/assets/builds/routes/index.js.map +7 -0
  52. data/examples/ruby-on-rails-api/descope/package-lock.json +7 -8
  53. data/examples/ruby-on-rails-api/descope/yarn.lock +3 -3
  54. data/lib/descope/api/v1/auth.rb +21 -6
  55. data/lib/descope/api/v1/management/access_key.rb +5 -4
  56. data/lib/descope/api/v1/management/common.rb +4 -1
  57. data/lib/descope/api/v1/management/role.rb +22 -6
  58. data/lib/descope/api/v1/management/user.rb +17 -0
  59. data/lib/descope/mixins/common.rb +2 -12
  60. data/lib/descope/mixins/http.rb +1 -1
  61. data/lib/descope/version.rb +1 -1
  62. data/spec/integration/lib.descope/api/v1/auth/enchantedlink_spec.rb +81 -0
  63. data/spec/integration/lib.descope/api/v1/auth/magiclink_spec.rb +49 -0
  64. data/spec/integration/lib.descope/api/v1/auth/otp_spec.rb +38 -0
  65. data/spec/integration/lib.descope/api/v1/auth/password_spec.rb +41 -0
  66. data/spec/integration/lib.descope/api/v1/auth/totp_spec.rb +76 -0
  67. data/spec/integration/lib.descope/api/v1/management/access_key_spec.rb +62 -0
  68. data/spec/integration/lib.descope/api/v1/management/audit_spec.rb +16 -0
  69. data/spec/integration/lib.descope/api/v1/management/authz_spec.rb +187 -0
  70. data/spec/integration/lib.descope/api/v1/management/flow_spec.rb +44 -0
  71. data/spec/integration/lib.descope/api/v1/management/permissions_spec.rb +27 -0
  72. data/spec/integration/lib.descope/api/v1/management/project_spec.rb +29 -0
  73. data/spec/integration/lib.descope/api/v1/management/roles_spec.rb +116 -0
  74. data/spec/integration/lib.descope/api/v1/management/user_spec.rb +262 -0
  75. data/spec/lib.descope/api/v1/auth_spec.rb +50 -1
  76. data/spec/lib.descope/api/v1/management/access_key_spec.rb +4 -2
  77. data/spec/lib.descope/api/v1/management/role_spec.rb +35 -6
  78. data/spec/lib.descope/api/v1/management/user_spec.rb +40 -0
  79. data/spec/spec_helper.rb +9 -38
  80. data/spec/support/client_config.rb +5 -1
  81. data/spec/support/dummy_class.rb +15 -1
  82. data/spec/support/utils.rb +1 -1
  83. metadata +56 -4
  84. data/examples/ruby-on-rails-api/descope/tmp/pids/.keep +0 -0
  85. data/examples/ruby-on-rails-api/descope/tmp/storage/.keep +0 -0
@@ -9854,10 +9854,9 @@
9854
9854
  }
9855
9855
  },
9856
9856
  "node_modules/ip": {
9857
- "version": "2.0.0",
9858
- "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
9859
- "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==",
9860
- "license": "MIT"
9857
+ "version": "2.0.1",
9858
+ "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz",
9859
+ "integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ=="
9861
9860
  },
9862
9861
  "node_modules/ipaddr.js": {
9863
9862
  "version": "2.1.0",
@@ -18386,16 +18385,16 @@
18386
18385
  }
18387
18386
  },
18388
18387
  "node_modules/typescript": {
18389
- "version": "5.3.3",
18390
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
18391
- "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
18388
+ "version": "4.9.5",
18389
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
18390
+ "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
18392
18391
  "peer": true,
18393
18392
  "bin": {
18394
18393
  "tsc": "bin/tsc",
18395
18394
  "tsserver": "bin/tsserver"
18396
18395
  },
18397
18396
  "engines": {
18398
- "node": ">=14.17"
18397
+ "node": ">=4.2.0"
18399
18398
  }
18400
18399
  },
18401
18400
  "node_modules/unbox-primitive": {
@@ -5680,9 +5680,9 @@ internal-slot@^1.0.5:
5680
5680
  side-channel "^1.0.4"
5681
5681
 
5682
5682
  ip@^2.0.0:
5683
- version "2.0.0"
5684
- resolved "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz"
5685
- integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==
5683
+ version "2.0.1"
5684
+ resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.1.tgz#e8f3595d33a3ea66490204234b77636965307105"
5685
+ integrity sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==
5686
5686
 
5687
5687
  ipaddr.js@1.9.1:
5688
5688
  version "1.9.1"
@@ -41,8 +41,21 @@ module Descope
41
41
  jwt_response
42
42
  end
43
43
 
44
- def exchange_access_key(access_key = nil)
45
- post(EXCHANGE_AUTH_ACCESS_KEY_PATH, {}, {}, access_key)
44
+ def exchange_access_key(access_key: nil, login_options: {}, audience: nil)
45
+ # Return a new session token for the given access key
46
+ # Args:
47
+ # access_key (str): The access key
48
+ # audience (str|Iterable[str]|nil): Optional recipients that the JWT is intended for
49
+ # (must be equal to the 'aud' claim on the provided token)
50
+ # login_options (hash): Optional advanced controls over login parameters
51
+ # Return value (Hash): returns the session token from the server together with the expiry and key id
52
+ # (sessionToken:Hash, keyId:str, expiration:int)
53
+ unless (access_key.is_a?(String) || access_key.nil?) && !access_key.to_s.empty?
54
+ raise Descope::AuthException, 'Access key should be a string!'
55
+ end
56
+
57
+ res = post(EXCHANGE_AUTH_ACCESS_KEY_PATH, { loginOptions: login_options, audience: }, {}, access_key)
58
+ generate_auth_info(res, nil, false, audience)
46
59
  end
47
60
 
48
61
  def select_tenant(tenant_id: nil, refresh_token: nil)
@@ -223,16 +236,18 @@ module Descope
223
236
 
224
237
  # validate the session token if sessionJwt is not empty
225
238
  st_jwt = response_body.fetch('sessionJwt', '')
226
- if st_jwt
227
- jwt_response[SESSION_TOKEN_NAME] = validate_token(st_jwt, audience)
239
+ unless st_jwt.empty?
240
+ @logger.debug "validating session token with refresh_token: #{refresh_token}" if st_jwt
241
+ jwt_response[SESSION_TOKEN_NAME] = validate_token(st_jwt, audience) if st_jwt
228
242
  end
229
243
 
230
244
  # validate refresh token if refresh_token was passed or if refreshJwt is not empty
231
245
  rt_jwt = response_body.fetch('refreshJwt', '')
232
246
 
233
- if refresh_token
247
+ if !refresh_token.nil? || !refresh_token.to_s.empty?
248
+ @logger.debug "validating refresh token: #{refresh_token}" if refresh_token
234
249
  jwt_response[REFRESH_SESSION_TOKEN_NAME] = validate_token(refresh_token, audience)
235
- elsif rt_jwt
250
+ elsif !rt_jwt.empty?
236
251
  jwt_response[REFRESH_SESSION_TOKEN_NAME] = validate_token(rt_jwt, audience)
237
252
  end
238
253
 
@@ -9,22 +9,23 @@ module Descope
9
9
  include Descope::Mixins::Validation
10
10
  include Descope::Api::V1::Management::Common
11
11
 
12
- def create_access_key(name: nil, expire_time: nil, role_names: nil, key_tenants: nil)
12
+ def create_access_key(name: nil, expire_time: nil, role_names: nil, key_tenants: nil, custom_claims: nil)
13
13
  # Create a new access key.'
14
14
  # @see https://docs.descope.com/api/openapi/accesskeymanagement/operation/CreateAccessKey/
15
15
 
16
16
  role_names ||= []
17
17
  key_tenants ||= []
18
18
  validate_tenants(key_tenants)
19
- post(ACCESS_KEY_CREATE_PATH, access_key_compose_create_body(name, expire_time, role_names, key_tenants))
19
+ post(ACCESS_KEY_CREATE_PATH, access_key_compose_create_body(name, expire_time, role_names, key_tenants, custom_claims))
20
20
  end
21
21
 
22
- def access_key_compose_create_body(name, expire_time, role_names, key_tenants)
22
+ def access_key_compose_create_body(name, expire_time, role_names, key_tenants, custom_claims)
23
23
  {
24
24
  name:,
25
25
  expireTime: expire_time,
26
26
  roleNames: role_names,
27
- keyTenants: associated_tenants_to_hash_array(key_tenants)
27
+ keyTenants: associated_tenants_to_hash_array(key_tenants),
28
+ customClaims: custom_claims
28
29
  }
29
30
  end
30
31
 
@@ -15,7 +15,7 @@ module Descope
15
15
  TENANT_SEARCH_ALL_PATH = '/v1/mgmt/tenant/search'
16
16
  PASSWORD_SETTINGS_PATH = '/v1/mgmt/password/settings'
17
17
 
18
- # userUSER_CREATE_PATH
18
+ # user
19
19
  USER_CREATE_PATH = '/v1/mgmt/user/create'
20
20
  USER_CREATE_BATCH_PATH = '/v1/mgmt/user/create/batch'
21
21
  USER_UPDATE_PATH = '/v1/mgmt/user/update'
@@ -34,6 +34,8 @@ module Descope
34
34
  USER_UPDATE_CUSTOM_ATTRIBUTE_PATH = '/v1/mgmt/user/update/customAttribute'
35
35
  USER_ADD_ROLE_PATH = '/v1/mgmt/user/update/role/add'
36
36
  USER_REMOVE_ROLE_PATH = '/v1/mgmt/user/update/role/remove'
37
+ USER_SET_TEMPORARY_PASSWORD_PATH = '/v1/mgmt/user/password/set/temporary'
38
+ USER_SET_ACTIVE_PASSWORD_PATH = '/v1/mgmt/user/password/set/active'
37
39
  USER_SET_PASSWORD_PATH = '/v1/mgmt/user/password/set'
38
40
  USER_EXPIRE_PASSWORD_PATH = '/v1/mgmt/user/password/expire'
39
41
  USER_ADD_TENANT_PATH = '/v1/mgmt/user/update/tenant/add'
@@ -80,6 +82,7 @@ module Descope
80
82
  ROLE_UPDATE_PATH = '/v1/mgmt/role/update'
81
83
  ROLE_DELETE_PATH = '/v1/mgmt/role/delete'
82
84
  ROLE_LOAD_ALL_PATH = '/v1/mgmt/role/all'
85
+ ROLE_SEARCH_PATH = '/v1/mgmt/role/search'
83
86
 
84
87
  # flow
85
88
  FLOW_LIST_PATH = '/v1/mgmt/flow/list'
@@ -8,18 +8,19 @@ module Descope
8
8
  module Role
9
9
  include Descope::Api::V1::Management::Common
10
10
 
11
- def create_role(name: nil, description: nil, permission_names: nil)
11
+ def create_role(name: nil, description: nil, permission_names: nil, tenant_id: nil)
12
12
  # Create a new role.
13
13
  permission_names ||= []
14
14
  request_params = {
15
15
  name:,
16
16
  description:,
17
- permissionNames: permission_names
17
+ permissionNames: permission_names,
18
+ tenantId: tenant_id
18
19
  }
19
20
  post(ROLE_CREATE_PATH, request_params)
20
21
  end
21
22
 
22
- def update_role(name: nil, new_name: nil, description: nil, permission_names: nil)
23
+ def update_role(name: nil, new_name: nil, description: nil, permission_names: nil, tenant_id: nil)
23
24
  # Update an existing role with the given various fields. IMPORTANT: All parameters are used as overrides
24
25
  # to the existing role. Empty fields will override populated fields. Use carefully.
25
26
  permission_names ||= []
@@ -27,20 +28,35 @@ module Descope
27
28
  name:,
28
29
  newName: new_name,
29
30
  description:,
30
- permissionNames: permission_names
31
+ permissionNames: permission_names,
32
+ tenantId: tenant_id
31
33
  }
32
34
  post(ROLE_UPDATE_PATH, request_params)
33
35
  end
34
36
 
35
- def delete_role(name)
37
+ def delete_role(name: nil, tenant_id: nil)
36
38
  # Delete an existing role. IMPORTANT: This action is irreversible. Use carefully.
37
- post(ROLE_DELETE_PATH, { name: })
39
+ raise Descope::ArgumentError, 'name is required' if name.nil? || name.empty?
40
+
41
+ request_params = { name: }
42
+ request_params[:tenantId] = tenant_id if tenant_id
43
+ post(ROLE_DELETE_PATH, request_params)
38
44
  end
39
45
 
40
46
  def load_all_roles
41
47
  # Load all roles.
42
48
  get(ROLE_LOAD_ALL_PATH)
43
49
  end
50
+
51
+ def search_roles(role_names: nil, tenant_ids: nil, role_name_like: nil, permission_names: nil)
52
+ # Search for roles using the given parameters.
53
+ request_params = {}
54
+ request_params[:roleNames] = role_names if role_names
55
+ request_params[:tenantIds] = tenant_ids if tenant_ids
56
+ request_params[:roleNameLike] = role_name_like if role_name_like
57
+ request_params[:permissionNames] = permission_names if permission_names
58
+ post(ROLE_SEARCH_PATH, request_params)
59
+ end
44
60
  end
45
61
  end
46
62
  end
@@ -385,6 +385,23 @@ module Descope
385
385
  post(Common::USER_REMOVE_TENANT_PATH, body)
386
386
  end
387
387
 
388
+ def set_temporary_password(login_id: nil, password: nil)
389
+ body = {
390
+ loginId: login_id,
391
+ password:
392
+ }
393
+ post(Common::USER_SET_TEMPORARY_PASSWORD_PATH, body)
394
+ end
395
+
396
+ def set_active_password(login_id: nil, password: nil)
397
+ body = {
398
+ loginId: login_id,
399
+ password:
400
+ }
401
+ post(Common::USER_SET_ACTIVE_PASSWORD_PATH, body)
402
+ end
403
+
404
+ # Deprecated (use set_temporary_password(..) instead)
388
405
  def set_password(login_id: nil, password: nil)
389
406
  body = {
390
407
  loginId: login_id,
@@ -9,7 +9,7 @@ module Descope
9
9
  DEFAULT_BASE_URL = 'https://api.descope.com' # pragma: no cover
10
10
  DEFAULT_TIMEOUT_SECONDS = 60
11
11
  DEFAULT_JWT_VALIDATION_LEEWAY = 5
12
- PHONE_REGEX = %r{^(?:\(?(?:00|\+)([1-4]\d\d|[1-9]\d?)\)?)?[-./ \\]?(?:(?:\(?\d{1,}\)?[-./ \\]?){0,})(?:[-./ \\]?(?:#|ext\.?|extension|x)[-./ \\]?(\d+))?$}.freeze
12
+ PHONE_REGEX = %r{^(?:\(?(?:00|\+)([1-4]\d\d|[1-9]\d?)\)?)?[-./ \\]?(?:(?:\(?\d{1,}\)?[-./ \\]?){0,})(?:[-./ \\]?(?:#|ext\.?|extension|x)[-./ \\]?(\d+))?$}
13
13
 
14
14
  SESSION_COOKIE_NAME = 'DS'
15
15
  REFRESH_SESSION_COOKIE_NAME = 'DSR'
@@ -50,7 +50,7 @@ module Descope
50
50
  VALIDATE_SESSION_PATH = '/v1/auth/validate'
51
51
  ME_PATH = '/v1/auth/me'
52
52
 
53
- # accesskey
53
+ # access key
54
54
  EXCHANGE_AUTH_ACCESS_KEY_PATH = '/v1/auth/accesskey/exchange'
55
55
 
56
56
  # otp
@@ -114,16 +114,6 @@ module Descope
114
114
  module EndpointsV2
115
115
  PUBLIC_KEY_PATH = '/v2/keys'
116
116
  end
117
-
118
- module LoginOptions
119
- attr_accessor :stepup, :mfa, :custom_claims
120
-
121
- def initialize
122
- @stepup = stepup || false
123
- @mfa ||= false
124
- @custom_claims ||= {}
125
- end
126
- end
127
117
  end
128
118
  end
129
119
  end
@@ -96,7 +96,7 @@ module Descope
96
96
 
97
97
  raise Descope::Unsupported.new("No response from server", code: 400) unless result && result.respond_to?(:code)
98
98
 
99
- @logger.info "http status code: #{result.code}"
99
+ @logger.info("API Request: [#{method}] #{uri} - Response Code: #{result.code}")
100
100
  case result.code
101
101
  when 200...226 then safe_parse_json(result.body)
102
102
  when 400 then raise Descope::BadRequest.new(result.body, code: result.code, headers: result.headers)
@@ -2,6 +2,6 @@
2
2
 
3
3
  # Current version of gem
4
4
  module Descope
5
- VERSION = '1.0.4'
5
+ VERSION = '1.0.5'
6
6
  SDK_VERSION = '1.0.0'
7
7
  end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ def poll_for_session(descope_client, pending_ref)
6
+ max_tries = 15
7
+ i = 0
8
+ done = false
9
+ while !done && i < max_tries
10
+ begin
11
+ i += 1
12
+ @client.logger.info('waiting 4 seconds for session to be created...')
13
+ sleep(4)
14
+ print '.'
15
+ @client.logger.info("Getting session for pending_ref: #{pending_ref}...")
16
+ jwt_response = descope_client.enchanted_link_get_session(pending_ref)
17
+ done = true
18
+ rescue Descope::AuthException, Descope::Unauthorized => e
19
+ @client.logger.info("Failed pending session, err: #{e}")
20
+ nil
21
+ end
22
+
23
+ next unless jwt_response
24
+
25
+ @client.logger.info("jwt_response: #{jwt_response}")
26
+ refresh_token = jwt_response[Descope::Mixins::Common::REFRESH_SESSION_TOKEN_NAME]['jwt']
27
+
28
+ @client.logger.info("refresh_token: #{refresh_token}")
29
+ done = true
30
+ return refresh_token
31
+ end
32
+ end
33
+
34
+ def verify_session(descope_client: nil, res: nil, user: nil)
35
+ raise StandardError, 'Missing required parameters' if descope_client.nil? || res.nil? || user.nil?
36
+
37
+ token = res['link'].match(/.+verify\?t=(.+)/)[1]
38
+ @client.logger.info("token: #{token}")
39
+
40
+ expect do
41
+ descope_client.enchanted_link_verify_token(token)
42
+ @client.logger.info('EnchantedLink Token Verified! now getting session information...')
43
+ @client.logger.info('Polling for session...')
44
+ refresh_token = poll_for_session(descope_client, res['pendingRef'])
45
+ my_details = descope_client.me(refresh_token)
46
+ expect(my_details['email']).to eq(user['email'])
47
+ @client.logger.info('EnchantedLink Token Verified via sign in!')
48
+ rescue StandardError => e
49
+ raise StandardError, "Verification failed - Could not verify token #{e.message}"
50
+
51
+ end.to_not raise_error
52
+ end
53
+
54
+ describe Descope::Api::V1::Auth::EnchantedLink do
55
+ before(:all) do
56
+ @client = DescopeClient.new(Configuration.config)
57
+ end
58
+
59
+ after(:all) do
60
+ @client.logger.info('Cleaning up test users...')
61
+ all_users = @client.search_all_users
62
+ all_users['users'].each do |user|
63
+ if user['middleName'] == 'Ruby SDK User'
64
+ @client.logger.info("Deleting ruby spec test user #{user['loginIds'][0]}")
65
+ @client.delete_user(user['loginIds'][0])
66
+ end
67
+ end
68
+ end
69
+
70
+ context 'test EnchantedLink for test user' do
71
+ it 'should sign in with enchanted link' do
72
+ user = build(:user)
73
+ test_user = @client.create_test_user(**user)['user']
74
+ @client.logger.info("Should sign in a test user => #{test_user['loginIds'][0]} with enchanted link...")
75
+ res = @client.generate_enchanted_link_for_test_user(login_id: test_user['loginIds'][0], uri: 'http://localhost:3000/verify')
76
+ @client.logger.info("res: #{res}")
77
+ @client.logger.info('Verifying session...')
78
+ verify_session(descope_client: @client, res:, user: test_user)
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Descope::Api::V1::Auth::MagicLink do
6
+ before(:all) do
7
+ @client = DescopeClient.new(Configuration.config)
8
+ end
9
+
10
+ after(:all) do
11
+ @client.logger.info('Cleaning up test users...')
12
+ all_users = @client.search_all_users
13
+ all_users['users'].each do |user|
14
+ if user['middleName'] == 'Ruby SDK User'
15
+ @client.logger.info("Deleting ruby spec test user #{user['loginIds'][0]}")
16
+ @client.delete_user(user['loginIds'][0])
17
+ end
18
+ end
19
+ end
20
+
21
+ context 'test Magiclink for test user' do
22
+ it 'should sign in with magiclink' do
23
+ user = build(:user)
24
+ test_user = @client.create_test_user(**user)['user']
25
+ @client.create_test_user(**user)
26
+ res = @client.generate_magic_link_for_test_user(
27
+ method: Descope::Mixins::Common::DeliveryMethod::EMAIL,
28
+ login_id: test_user['loginIds'][0],
29
+ uri: 'http://localhost:3000/verify'
30
+ )
31
+ @client.logger.info("res: #{res}")
32
+ token = res['link'].match(/^http.+verify\?t=(.+)/)[1]
33
+ @client.logger.info("token: #{token}")
34
+
35
+ expect do
36
+ @client.logger.info('Verifying token...')
37
+ jwt_response = @client.magiclink_verify_token(token)
38
+ @client.logger.info("jwt_response #{jwt_response}")
39
+ my_details = @client.me(jwt_response['refreshSessionToken']['jwt'])
40
+ @client.logger.info('verifying session...')
41
+ expect(my_details['email']).to eq(test_user['email'])
42
+ @client.logger.info('Magiclink Token Verified via sign in!')
43
+ rescue StandardError => e
44
+ raise StandardError, "Verification failed - Could not verify token: #{e.message}"
45
+
46
+ end.to_not raise_error
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Descope::Api::V1::Auth::OTP do
6
+ before(:all) do
7
+ @client = DescopeClient.new(Configuration.config)
8
+ end
9
+
10
+ after(:all) do
11
+ @client.logger.info('Cleaning up test users...')
12
+ all_users = @client.search_all_users
13
+ all_users['users'].each do |user|
14
+ if user['middleName'] == 'Ruby SDK User'
15
+ @client.logger.info("Deleting ruby spec test user #{user['loginIds'][0]}")
16
+ @client.delete_user(user['loginIds'][0])
17
+ end
18
+ end
19
+ end
20
+
21
+ context 'test otp sign-in with test user' do
22
+ it 'should sign in with otp' do
23
+ user = build(:user)
24
+ test_user = @client.create_test_user(**user)['user']
25
+ @client.create_test_user(**user)
26
+ res = @client.generate_otp_for_test_user(
27
+ method: Descope::Mixins::Common::DeliveryMethod::EMAIL,
28
+ login_id: test_user['loginIds'][0]
29
+ )
30
+ @client.logger.info("res: #{res}")
31
+ @client.otp_verify_code(
32
+ method: Descope::Mixins::Common::DeliveryMethod::EMAIL,
33
+ login_id: user[:login_id],
34
+ code: res['code']
35
+ )
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'rotp'
5
+
6
+ describe Descope::Api::V1::Auth::Password do
7
+ before(:all) do
8
+ @password = SpecUtils.generate_password
9
+ @new_password = SpecUtils.generate_password
10
+ @user = build(:user)
11
+ @client = DescopeClient.new(Configuration.config)
12
+ end
13
+
14
+ context 'test password methods' do
15
+ it 'should get password policy' do
16
+ # Get the configured password policy for the project.
17
+ res = @client.get_password_policy
18
+ @client.logger.info("Password policy: #{res}")
19
+ end
20
+
21
+ it 'should sign up with password' do
22
+ res = @client.password_sign_up(login_id: @user[:login_id], password: @password, user: @user)
23
+ expect { res }.not_to raise_error
24
+ end
25
+
26
+ it 'should sign in with password' do
27
+ res = @client.password_sign_in(login_id: @user[:login_id], password: @password)
28
+ expect { res }.not_to raise_error
29
+ end
30
+
31
+ it 'should replace the password' do
32
+ res = @client.password_replace(login_id: @user[:login_id], old_password: @password, new_password: @new_password)
33
+ expect { res }.not_to raise_error
34
+ end
35
+
36
+ it 'should login with new password' do
37
+ res = @client.password_sign_in(login_id: @user[:login_id], password: @new_password)
38
+ expect { res }.not_to raise_error
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'rotp'
5
+
6
+ describe Descope::Api::V1::Auth::TOTP do
7
+ before(:all) do
8
+ @client = DescopeClient.new(Configuration.config)
9
+ end
10
+
11
+ after(:all) do
12
+ @client.logger.info('Cleaning up test users...')
13
+ all_users = @client.search_all_users
14
+ all_users['users'].each do |user|
15
+ if user['middleName'] == 'Ruby SDK User'
16
+ @client.logger.info("Deleting ruby spec test user #{user['loginIds'][0]}")
17
+ @client.delete_user(user['loginIds'][0])
18
+ end
19
+ end
20
+ end
21
+
22
+ context 'test totp methods' do
23
+ it 'should sign up with totp' do
24
+ # Initiate a TOTP sign-up process for a new end user.
25
+ # Descope will generate a TOTP key (also called a secret or seed) that will be entered into the end user's
26
+ # authenticator app so that TOTP codes can be successfully verified.
27
+ # The new end user will be registered after the full TOTP sign-up flow has successfully completed.
28
+
29
+ user = build(:user)
30
+
31
+ @client.logger.info('1. Sign up TOTP')
32
+ totp_key = @client.totp_sign_up(login_id: user[:login_id], user:)['key']
33
+ totp = ROTP::TOTP.new(totp_key)
34
+ p "Current OTP: #{totp.now}"
35
+
36
+ @client.logger.info('2. TOTP sign in')
37
+ login_res = @client.totp_sign_in_code(login_id: user[:login_id], code: totp.now)
38
+ @client.logger.info("login_res: #{login_res}")
39
+ refresh_token = login_res['refreshSessionToken']['jwt']
40
+
41
+ @client.logger.info('3. Verify email')
42
+ my_details = @client.me(refresh_token)
43
+ expect(my_details['email']).to eq(user[:email])
44
+ end
45
+
46
+ it 'should add or update totp key' do
47
+ # Add or update TOTP key for existing end user
48
+ # Update the email address of an end user, after verifying the authenticity of the end user using OTP.
49
+
50
+ user = build(:user)
51
+
52
+ @client.logger.info('1. Sign up TOTP')
53
+ totp_key = @client.totp_sign_up(login_id: user[:login_id], user:)['key']
54
+ totp = ROTP::TOTP.new(totp_key)
55
+ p "Current OTP: #{totp.now}"
56
+
57
+ @client.logger.info('2. TOTP sign in')
58
+ login_res = @client.totp_sign_in_code(login_id: user[:login_id], code: totp.now)
59
+ @client.logger.info("login_res: #{login_res}")
60
+ refresh_token = login_res['refreshSessionToken']['jwt']
61
+
62
+ @client.logger.info('3. Add or update TOTP key')
63
+ new_key = @client.totp_add_update_key(login_id: user[:login_id], refresh_token:)['key']
64
+ new_totp = ROTP::TOTP.new(new_key)
65
+ p "New OTP: #{totp.now}"
66
+
67
+ @client.logger.info('4. TOTP sign in with new key')
68
+ login_res = @client.totp_sign_in_code(login_id: user[:login_id], code: new_totp.now)
69
+ refresh_token = login_res['refreshSessionToken']['jwt']
70
+
71
+ @client.logger.info('5. Verify email')
72
+ my_details = @client.me(refresh_token)
73
+ expect(my_details['email']).to eq(user[:email])
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Descope::Api::V1::Management::AccessKey do
6
+ before(:all) do
7
+ @client = DescopeClient.new(Configuration.config)
8
+ end
9
+
10
+ context 'perform access key methods like create, delete load' do
11
+ before(:all) do
12
+ @key_name = 'Ruby SDK Test Key'
13
+
14
+ keys = @client.search_all_access_keys['keys']
15
+ keys.each do |key|
16
+ if key['name'] == @key_name
17
+ @client.delete_access_key(key['id'])
18
+ @client.logger.info("deleting test access key #{@key_name}")
19
+ end
20
+ end
21
+
22
+ res = @client.search_all_tenants(names: ['some-new-tenant'])
23
+ res['tenants'].each do |tenant|
24
+ @client.delete_tenant(tenant['id'])
25
+ end
26
+
27
+ @client.logger.info('Creating tenant with name: some-new-tenant')
28
+ @tenant_id = @client.create_tenant(name: 'some-new-tenant')['id']
29
+ @client.logger.info('creating access key')
30
+ @access_key = @client.create_access_key(name: @key_name, key_tenants: [{ tenant_id: @tenant_id }])
31
+ sleep 60
32
+ end
33
+
34
+ it 'should create the access key and load it' do
35
+ response = @client.load_access_key(@access_key['key']['id'])
36
+ expect(response['key']['name']).to eq(@key_name)
37
+ end
38
+
39
+ it 'should update the access key' do
40
+ new_name = 'Ruby SDK Test Key Updated'
41
+ @client.logger.info("access key id: #{@access_key['key']['id']}")
42
+ response = @client.update_access_key(id: @access_key['key']['id'], name: new_name)
43
+ expect(response['key']['name']).to eq(new_name)
44
+ end
45
+
46
+ it 'should deactivate the access key' do
47
+ response = @client.deactivate_access_key(@access_key['key']['id'])
48
+ @client.logger.info("deactivate key response: #{response}")
49
+ # expect(response['key']['status']).to eq('DEACTIVATED')
50
+ end
51
+
52
+ it 'should activate the access key' do
53
+ response = @client.activate_access_key(@access_key['key']['id'])
54
+ @client.logger.info("activate key response: #{response}")
55
+ end
56
+
57
+ after(:all) do
58
+ @client.delete_access_key(@access_key['key']['id'])
59
+ @client.delete_tenant(@tenant_id)
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Descope::Api::V1::Management::Audit do
6
+ before(:all) do
7
+ @client = DescopeClient.new(Configuration.config)
8
+ end
9
+
10
+
11
+ it 'should search the audit trail for user operations' do
12
+ res = @client.audit_search(actions: ['LoginSucceed'])
13
+ expect(res).to be_a(Hash)
14
+ expect(res['audits']).to be_a(Array)
15
+ end
16
+ end