descope 1.0.4 → 1.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yaml +15 -27
- data/.github/workflows/publish-gem.yaml +36 -0
- data/Gemfile +12 -14
- data/Gemfile.lock +40 -87
- data/README.md +39 -17
- data/examples/ruby/access_key_app.rb +4 -2
- data/examples/ruby/management/Gemfile.lock +2 -2
- data/examples/ruby/management/role_app.rb +8 -3
- data/examples/ruby-on-rails-api/descope/.gitignore +58 -28
- data/examples/ruby-on-rails-api/descope/Gemfile.lock +3 -3
- data/examples/ruby-on-rails-api/descope/app/assets/builds/App.css +62 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/App.css.map +7 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/application.css +62 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/application.css.map +7 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/application.js +40369 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/application.js.map +7 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/components/App.css +62 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/components/App.css.map +7 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/components/App.js +27979 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/components/App.js.map +7 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/components/Dashboard.css +62 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/components/Dashboard.css.map +7 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/components/Dashboard.js +27118 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/components/Dashboard.js.map +7 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/components/Home.css +62 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/components/Home.css.map +7 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/components/Home.js +27113 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/components/Home.js.map +7 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/components/Login.css +62 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/components/Login.css.map +7 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/components/Login.js +27131 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/components/Login.js.map +7 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/components/Profile.css +62 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/components/Profile.css.map +7 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/components/Profile.js +27168 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/components/Profile.js.map +7 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/components/index.css +62 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/components/index.css.map +7 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/components/index.js +28250 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/components/index.js.map +7 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/controllers/application.js +2456 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/controllers/application.js.map +7 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/controllers/index.js +2453 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/controllers/index.js.map +7 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/reportWebVitals.js +211 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/reportWebVitals.js.map +7 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/routes/index.css +62 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/routes/index.css.map +7 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/routes/index.js +27973 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/routes/index.js.map +7 -0
- data/examples/ruby-on-rails-api/descope/package-lock.json +7 -8
- data/examples/ruby-on-rails-api/descope/yarn.lock +3 -3
- data/lib/descope/api/v1/auth.rb +21 -6
- data/lib/descope/api/v1/management/access_key.rb +5 -4
- data/lib/descope/api/v1/management/common.rb +4 -1
- data/lib/descope/api/v1/management/role.rb +22 -6
- data/lib/descope/api/v1/management/user.rb +17 -0
- data/lib/descope/mixins/common.rb +2 -12
- data/lib/descope/mixins/http.rb +1 -1
- data/lib/descope/version.rb +1 -1
- data/spec/integration/lib.descope/api/v1/auth/enchantedlink_spec.rb +81 -0
- data/spec/integration/lib.descope/api/v1/auth/magiclink_spec.rb +49 -0
- data/spec/integration/lib.descope/api/v1/auth/otp_spec.rb +38 -0
- data/spec/integration/lib.descope/api/v1/auth/password_spec.rb +41 -0
- data/spec/integration/lib.descope/api/v1/auth/totp_spec.rb +76 -0
- data/spec/integration/lib.descope/api/v1/management/access_key_spec.rb +62 -0
- data/spec/integration/lib.descope/api/v1/management/audit_spec.rb +16 -0
- data/spec/integration/lib.descope/api/v1/management/authz_spec.rb +187 -0
- data/spec/integration/lib.descope/api/v1/management/flow_spec.rb +44 -0
- data/spec/integration/lib.descope/api/v1/management/permissions_spec.rb +27 -0
- data/spec/integration/lib.descope/api/v1/management/project_spec.rb +29 -0
- data/spec/integration/lib.descope/api/v1/management/roles_spec.rb +116 -0
- data/spec/integration/lib.descope/api/v1/management/user_spec.rb +262 -0
- data/spec/lib.descope/api/v1/auth_spec.rb +50 -1
- data/spec/lib.descope/api/v1/management/access_key_spec.rb +4 -2
- data/spec/lib.descope/api/v1/management/role_spec.rb +35 -6
- data/spec/lib.descope/api/v1/management/user_spec.rb +40 -0
- data/spec/spec_helper.rb +9 -38
- data/spec/support/client_config.rb +5 -1
- data/spec/support/dummy_class.rb +15 -1
- data/spec/support/utils.rb +1 -1
- metadata +56 -4
- data/examples/ruby-on-rails-api/descope/tmp/pids/.keep +0 -0
- 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.
|
9858
|
-
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.
|
9859
|
-
"integrity": "sha512-
|
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": "
|
18390
|
-
"resolved": "https://registry.npmjs.org/typescript/-/typescript-
|
18391
|
-
"integrity": "sha512-
|
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": ">=
|
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.
|
5684
|
-
resolved "https://registry.
|
5685
|
-
integrity sha512-
|
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"
|
data/lib/descope/api/v1/auth.rb
CHANGED
@@ -41,8 +41,21 @@ module Descope
|
|
41
41
|
jwt_response
|
42
42
|
end
|
43
43
|
|
44
|
-
def exchange_access_key(access_key
|
45
|
-
|
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
|
-
|
227
|
-
|
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
|
-
#
|
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
|
-
|
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+))?$}
|
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
|
-
#
|
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
|
data/lib/descope/mixins/http.rb
CHANGED
@@ -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
|
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)
|
data/lib/descope/version.rb
CHANGED
@@ -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
|