descope 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.github/workflows/ci.yaml +54 -0
- data/.gitignore +59 -0
- data/.release-please-manifest.json +3 -0
- data/.rubocop.yml +10 -0
- data/.rubocop_todo.yml +10 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +90 -0
- data/Gemfile +22 -0
- data/Gemfile.lock +204 -0
- data/LICENSE +21 -0
- data/README.md +1171 -0
- data/Rakefile +31 -0
- data/descope.gemspec +34 -0
- data/examples/ruby/Gemfile +4 -0
- data/examples/ruby/Gemfile.lock +41 -0
- data/examples/ruby/access_key_app.rb +45 -0
- data/examples/ruby/enchantedlink_app.rb +65 -0
- data/examples/ruby/magiclink_app.rb +81 -0
- data/examples/ruby/management/Gemfile +5 -0
- data/examples/ruby/management/Gemfile.lock +38 -0
- data/examples/ruby/management/access_key_app.rb +71 -0
- data/examples/ruby/management/audit_app.rb +25 -0
- data/examples/ruby/management/authz_app.rb +135 -0
- data/examples/ruby/management/authz_files.json +229 -0
- data/examples/ruby/management/flow_app.rb +57 -0
- data/examples/ruby/management/permission_app.rb +56 -0
- data/examples/ruby/management/role_app.rb +58 -0
- data/examples/ruby/management/tenant_app.rb +60 -0
- data/examples/ruby/management/user_app.rb +60 -0
- data/examples/ruby/oauth_app.rb +39 -0
- data/examples/ruby/otp_app.rb +50 -0
- data/examples/ruby/password_app.rb +76 -0
- data/examples/ruby/saml_app.rb +38 -0
- data/examples/ruby-on-rails-api/descope/.dockerignore +37 -0
- data/examples/ruby-on-rails-api/descope/.gitattributes +9 -0
- data/examples/ruby-on-rails-api/descope/.gitignore +40 -0
- data/examples/ruby-on-rails-api/descope/.node-version +1 -0
- data/examples/ruby-on-rails-api/descope/.ruby-version +1 -0
- data/examples/ruby-on-rails-api/descope/Dockerfile +75 -0
- data/examples/ruby-on-rails-api/descope/Gemfile +67 -0
- data/examples/ruby-on-rails-api/descope/Gemfile.lock +284 -0
- data/examples/ruby-on-rails-api/descope/Procfile.dev +3 -0
- data/examples/ruby-on-rails-api/descope/README.md +54 -0
- data/examples/ruby-on-rails-api/descope/Rakefile +6 -0
- data/examples/ruby-on-rails-api/descope/app/assets/builds/.keep +0 -0
- data/examples/ruby-on-rails-api/descope/app/assets/config/manifest.js +3 -0
- data/examples/ruby-on-rails-api/descope/app/assets/images/.keep +0 -0
- data/examples/ruby-on-rails-api/descope/app/assets/images/descope.jpeg +0 -0
- data/examples/ruby-on-rails-api/descope/app/assets/images/favicon.ico +0 -0
- data/examples/ruby-on-rails-api/descope/app/assets/images/logo192.png +0 -0
- data/examples/ruby-on-rails-api/descope/app/assets/images/logo512.png +0 -0
- data/examples/ruby-on-rails-api/descope/app/assets/stylesheets/application.bootstrap.scss +67 -0
- data/examples/ruby-on-rails-api/descope/app/channels/application_cable/channel.rb +4 -0
- data/examples/ruby-on-rails-api/descope/app/channels/application_cable/connection.rb +4 -0
- data/examples/ruby-on-rails-api/descope/app/controllers/application_controller.rb +2 -0
- data/examples/ruby-on-rails-api/descope/app/controllers/concerns/.keep +0 -0
- data/examples/ruby-on-rails-api/descope/app/controllers/homepage_controller.rb +4 -0
- data/examples/ruby-on-rails-api/descope/app/controllers/session_controller.rb +66 -0
- data/examples/ruby-on-rails-api/descope/app/helpers/application_helper.rb +2 -0
- data/examples/ruby-on-rails-api/descope/app/helpers/homepage_helper.rb +2 -0
- data/examples/ruby-on-rails-api/descope/app/helpers/session_helper.rb +2 -0
- data/examples/ruby-on-rails-api/descope/app/javascript/App.css +53 -0
- data/examples/ruby-on-rails-api/descope/app/javascript/application.js +5 -0
- data/examples/ruby-on-rails-api/descope/app/javascript/components/App.jsx +4 -0
- data/examples/ruby-on-rails-api/descope/app/javascript/components/Dashboard.jsx +60 -0
- data/examples/ruby-on-rails-api/descope/app/javascript/components/Home.jsx +27 -0
- data/examples/ruby-on-rails-api/descope/app/javascript/components/Login.jsx +45 -0
- data/examples/ruby-on-rails-api/descope/app/javascript/components/Profile.jsx +81 -0
- data/examples/ruby-on-rails-api/descope/app/javascript/components/index.html +11 -0
- data/examples/ruby-on-rails-api/descope/app/javascript/components/index.jsx +24 -0
- data/examples/ruby-on-rails-api/descope/app/javascript/controllers/application.js +9 -0
- data/examples/ruby-on-rails-api/descope/app/javascript/controllers/index.js +5 -0
- data/examples/ruby-on-rails-api/descope/app/javascript/reportWebVitals.js +13 -0
- data/examples/ruby-on-rails-api/descope/app/javascript/routes/index.jsx +17 -0
- data/examples/ruby-on-rails-api/descope/app/jobs/application_job.rb +7 -0
- data/examples/ruby-on-rails-api/descope/app/mailers/application_mailer.rb +4 -0
- data/examples/ruby-on-rails-api/descope/app/models/application_record.rb +3 -0
- data/examples/ruby-on-rails-api/descope/app/models/concerns/.keep +0 -0
- data/examples/ruby-on-rails-api/descope/app/views/homepage/index.html.erb +2 -0
- data/examples/ruby-on-rails-api/descope/app/views/layouts/application.html.erb +16 -0
- data/examples/ruby-on-rails-api/descope/app/views/layouts/mailer.html.erb +13 -0
- data/examples/ruby-on-rails-api/descope/app/views/layouts/mailer.text.erb +1 -0
- data/examples/ruby-on-rails-api/descope/app/views/session/index.html.erb +2 -0
- data/examples/ruby-on-rails-api/descope/bin/bundle +109 -0
- data/examples/ruby-on-rails-api/descope/bin/dev +11 -0
- data/examples/ruby-on-rails-api/descope/bin/docker-entrypoint +8 -0
- data/examples/ruby-on-rails-api/descope/bin/rails +4 -0
- data/examples/ruby-on-rails-api/descope/bin/rake +4 -0
- data/examples/ruby-on-rails-api/descope/bin/setup +36 -0
- data/examples/ruby-on-rails-api/descope/build.js +30 -0
- data/examples/ruby-on-rails-api/descope/config/application.rb +42 -0
- data/examples/ruby-on-rails-api/descope/config/boot.rb +4 -0
- data/examples/ruby-on-rails-api/descope/config/cable.yml +10 -0
- data/examples/ruby-on-rails-api/descope/config/config.yml +9 -0
- data/examples/ruby-on-rails-api/descope/config/credentials.yml.enc +1 -0
- data/examples/ruby-on-rails-api/descope/config/database.yml +25 -0
- data/examples/ruby-on-rails-api/descope/config/environment.rb +5 -0
- data/examples/ruby-on-rails-api/descope/config/environments/development.rb +76 -0
- data/examples/ruby-on-rails-api/descope/config/environments/production.rb +97 -0
- data/examples/ruby-on-rails-api/descope/config/environments/test.rb +64 -0
- data/examples/ruby-on-rails-api/descope/config/initializers/assets.rb +13 -0
- data/examples/ruby-on-rails-api/descope/config/initializers/content_security_policy.rb +25 -0
- data/examples/ruby-on-rails-api/descope/config/initializers/filter_parameter_logging.rb +8 -0
- data/examples/ruby-on-rails-api/descope/config/initializers/inflections.rb +16 -0
- data/examples/ruby-on-rails-api/descope/config/initializers/load_config.rb +12 -0
- data/examples/ruby-on-rails-api/descope/config/initializers/permissions_policy.rb +13 -0
- data/examples/ruby-on-rails-api/descope/config/locales/en.yml +31 -0
- data/examples/ruby-on-rails-api/descope/config/puma.rb +35 -0
- data/examples/ruby-on-rails-api/descope/config/routes.rb +18 -0
- data/examples/ruby-on-rails-api/descope/config/storage.yml +34 -0
- data/examples/ruby-on-rails-api/descope/config.ru +6 -0
- data/examples/ruby-on-rails-api/descope/db/seeds.rb +9 -0
- data/examples/ruby-on-rails-api/descope/lib/assets/.keep +0 -0
- data/examples/ruby-on-rails-api/descope/lib/tasks/.keep +0 -0
- data/examples/ruby-on-rails-api/descope/log/.keep +0 -0
- data/examples/ruby-on-rails-api/descope/package-lock.json +19680 -0
- data/examples/ruby-on-rails-api/descope/package.json +51 -0
- data/examples/ruby-on-rails-api/descope/public/404.html +67 -0
- data/examples/ruby-on-rails-api/descope/public/422.html +67 -0
- data/examples/ruby-on-rails-api/descope/public/500.html +66 -0
- data/examples/ruby-on-rails-api/descope/public/apple-touch-icon-precomposed.png +0 -0
- data/examples/ruby-on-rails-api/descope/public/apple-touch-icon.png +0 -0
- data/examples/ruby-on-rails-api/descope/public/favicon.ico +0 -0
- data/examples/ruby-on-rails-api/descope/public/robots.txt +1 -0
- data/examples/ruby-on-rails-api/descope/storage/.keep +0 -0
- data/examples/ruby-on-rails-api/descope/tmp/.keep +0 -0
- data/examples/ruby-on-rails-api/descope/tmp/pids/.keep +0 -0
- data/examples/ruby-on-rails-api/descope/tmp/storage/.keep +0 -0
- data/examples/ruby-on-rails-api/descope/vendor/.keep +0 -0
- data/examples/ruby-on-rails-api/descope/yarn.lock +10780 -0
- data/lib/descope/api/v1/auth/enchantedlink.rb +156 -0
- data/lib/descope/api/v1/auth/magiclink.rb +170 -0
- data/lib/descope/api/v1/auth/oauth.rb +72 -0
- data/lib/descope/api/v1/auth/otp.rb +186 -0
- data/lib/descope/api/v1/auth/password.rb +100 -0
- data/lib/descope/api/v1/auth/saml.rb +48 -0
- data/lib/descope/api/v1/auth/totp.rb +72 -0
- data/lib/descope/api/v1/auth.rb +452 -0
- data/lib/descope/api/v1/management/access_key.rb +81 -0
- data/lib/descope/api/v1/management/audit.rb +82 -0
- data/lib/descope/api/v1/management/authz.rb +165 -0
- data/lib/descope/api/v1/management/common.rb +147 -0
- data/lib/descope/api/v1/management/flow.rb +55 -0
- data/lib/descope/api/v1/management/password.rb +58 -0
- data/lib/descope/api/v1/management/permission.rb +48 -0
- data/lib/descope/api/v1/management/project.rb +53 -0
- data/lib/descope/api/v1/management/role.rb +48 -0
- data/lib/descope/api/v1/management/scim.rb +206 -0
- data/lib/descope/api/v1/management/sso_settings.rb +153 -0
- data/lib/descope/api/v1/management/tenant.rb +71 -0
- data/lib/descope/api/v1/management/user.rb +619 -0
- data/lib/descope/api/v1/management.rb +38 -0
- data/lib/descope/api/v1/session.rb +84 -0
- data/lib/descope/api/v1.rb +13 -0
- data/lib/descope/client.rb +6 -0
- data/lib/descope/exception.rb +50 -0
- data/lib/descope/mixins/common.rb +129 -0
- data/lib/descope/mixins/headers.rb +15 -0
- data/lib/descope/mixins/http.rb +133 -0
- data/lib/descope/mixins/initializer.rb +80 -0
- data/lib/descope/mixins/logging.rb +30 -0
- data/lib/descope/mixins/validation.rb +79 -0
- data/lib/descope/mixins.rb +22 -0
- data/lib/descope/version.rb +7 -0
- data/lib/descope.rb +9 -0
- data/lib/descope_client.rb +5 -0
- data/release-please-config.json +18 -0
- data/renovate.json +6 -0
- data/spec/factories/user.rb +16 -0
- data/spec/lib.descope/api/v1/auth/enchantedlink_spec.rb +159 -0
- data/spec/lib.descope/api/v1/auth/magiclink_spec.rb +282 -0
- data/spec/lib.descope/api/v1/auth/oauth_spec.rb +117 -0
- data/spec/lib.descope/api/v1/auth/otp_spec.rb +285 -0
- data/spec/lib.descope/api/v1/auth/password_spec.rb +124 -0
- data/spec/lib.descope/api/v1/auth/saml_spec.rb +55 -0
- data/spec/lib.descope/api/v1/auth/totp_spec.rb +70 -0
- data/spec/lib.descope/api/v1/auth_spec.rb +372 -0
- data/spec/lib.descope/api/v1/management/access_key_spec.rb +118 -0
- data/spec/lib.descope/api/v1/management/audit_spec.rb +78 -0
- data/spec/lib.descope/api/v1/management/authz_spec.rb +336 -0
- data/spec/lib.descope/api/v1/management/flow_spec.rb +78 -0
- data/spec/lib.descope/api/v1/management/password_spec.rb +25 -0
- data/spec/lib.descope/api/v1/management/permission_spec.rb +81 -0
- data/spec/lib.descope/api/v1/management/project_spec.rb +63 -0
- data/spec/lib.descope/api/v1/management/role_spec.rb +85 -0
- data/spec/lib.descope/api/v1/management/scim_spec.rb +312 -0
- data/spec/lib.descope/api/v1/management/sso_settings_spec.rb +172 -0
- data/spec/lib.descope/api/v1/management/tenant_spec.rb +141 -0
- data/spec/lib.descope/api/v1/management/user_spec.rb +667 -0
- data/spec/lib.descope/api/v1/session_spec.rb +117 -0
- data/spec/lib.descope/client_spec.rb +40 -0
- data/spec/spec_helper.rb +72 -0
- data/spec/support/client_config.rb +14 -0
- data/spec/support/dummy_class.rb +36 -0
- data/spec/support/utils.rb +32 -0
- metadata +420 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Descope
|
|
4
|
+
module Api
|
|
5
|
+
module V1
|
|
6
|
+
# Holds all session methods
|
|
7
|
+
module Session
|
|
8
|
+
include Descope::Mixins::Common
|
|
9
|
+
include Descope::Mixins::Common::EndpointsV1
|
|
10
|
+
include Descope::Mixins::Common::EndpointsV2
|
|
11
|
+
|
|
12
|
+
def token_validation_key(project_id)
|
|
13
|
+
get("#{PUBLIC_KEY_PATH}/#{project_id}")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def refresh_session(refresh_token: nil, audience: nil)
|
|
17
|
+
# Validate a session token. Call this function for every incoming request to your
|
|
18
|
+
# private endpoints. Alternatively, use validate_and_refresh_session in order to
|
|
19
|
+
# automatically refresh expired sessions. If you need to use these specific claims
|
|
20
|
+
# [amr, drn, exp, iss, rexp, sub, jwt] in the top level of the response dict, please use
|
|
21
|
+
# them from the sessionToken key instead, as these claims will soon be deprecated from the top level
|
|
22
|
+
# of the response dict.
|
|
23
|
+
|
|
24
|
+
validate_refresh_token_not_nil(refresh_token)
|
|
25
|
+
validate_token(refresh_token, audience)
|
|
26
|
+
res = post(REFRESH_TOKEN_PATH, {}, {}, refresh_token)
|
|
27
|
+
generate_jwt_response(response_body: res, refresh_cookie: refresh_token, audience:)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def me(refresh_token = nil)
|
|
31
|
+
get(ME_PATH, {}, {}, refresh_token)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def sign_out(refresh_token = nil)
|
|
35
|
+
post(LOGOUT_PATH, {}, {}, refresh_token)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def sign_out_all(refresh_token = nil)
|
|
39
|
+
post(LOGOUT_ALL_PATH, {}, {}, refresh_token)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def validate_session(session_token: nil, audience: nil)
|
|
43
|
+
# Validate a session token. Call this function for every incoming request to your
|
|
44
|
+
# private endpoints. Alternatively, use validate_and_refresh_session in order to
|
|
45
|
+
# automatically refresh expired sessions. If you need to use these specific claims
|
|
46
|
+
# [amr, drn, exp, iss, rexp, sub, jwt] in the top level of the response dict, please use
|
|
47
|
+
# them from the sessionToken key instead, as these claims will soon be deprecated from the top level
|
|
48
|
+
# of the response dict.
|
|
49
|
+
# Return a hash includes the session token and all JWT claims
|
|
50
|
+
|
|
51
|
+
if session_token.nil? || session_token.empty?
|
|
52
|
+
raise Descope::AuthException.new('Session token is required for validation', code: 400)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
@logger.debug("Validating session token: #{session_token}")
|
|
56
|
+
res = validate_token(session_token, audience)
|
|
57
|
+
@logger.debug("Session token validation response: #{res}")
|
|
58
|
+
# Duplicate for saving backward compatibility but keep the same structure as the refresh operation response
|
|
59
|
+
res[SESSION_TOKEN_NAME] = deep_copy(res)
|
|
60
|
+
session_props = adjust_properties(res, true)
|
|
61
|
+
@logger.debug("session validation jwt response properties: #{session_props}")
|
|
62
|
+
session_props
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def validate_and_refresh_session(session_token: nil, refresh_token: nil, audience: nil)
|
|
66
|
+
# Validate the session token and refresh it if it has expired, the session token will automatically be refreshed.
|
|
67
|
+
# Either the session_token or the refresh_token must be provided.
|
|
68
|
+
# Call this function for every incoming request to your
|
|
69
|
+
# private endpoints. Alternatively, use validate_session to only validate the session.
|
|
70
|
+
|
|
71
|
+
raise Descope::AuthException.new('Session token is missing', code: 400) if session_token.nil?
|
|
72
|
+
|
|
73
|
+
begin
|
|
74
|
+
@logger.debug("Validating session token: #{session_token}")
|
|
75
|
+
validate_session(session_token:, audience:)
|
|
76
|
+
rescue Descope::AuthException
|
|
77
|
+
@logger.debug("Session is invalid, refreshing session with refresh token: #{refresh_token}")
|
|
78
|
+
refresh_session(refresh_token:, audience:)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require 'descope/api/v1/management'
|
|
2
|
+
require 'descope/api/v1/session'
|
|
3
|
+
require 'descope/api/v1/auth'
|
|
4
|
+
|
|
5
|
+
module Descope
|
|
6
|
+
module Api
|
|
7
|
+
module V1
|
|
8
|
+
include Descope::Api::V1::Management
|
|
9
|
+
include Descope::Api::V1::Session
|
|
10
|
+
include Descope::Api::V1::Auth
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
module Descope
|
|
2
|
+
# Default exception in namespace of Descope
|
|
3
|
+
# If you want to catch all exceptions, then you should use this one.
|
|
4
|
+
# Network exceptions are not included
|
|
5
|
+
class Exception < StandardError
|
|
6
|
+
attr_reader :error_data
|
|
7
|
+
|
|
8
|
+
def initialize(message, error_data = {})
|
|
9
|
+
super(message)
|
|
10
|
+
@error_data = error_data
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Parent for all exceptions that arise out of HTTP error responses.
|
|
15
|
+
class HTTPError < Descope::Exception
|
|
16
|
+
def headers
|
|
17
|
+
error_data[:headers]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def http_code
|
|
21
|
+
error_data[:code]
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class AuthException < Descope::Exception; end
|
|
26
|
+
# exception for unauthorized requests, if you see it,
|
|
27
|
+
# probably Bearer Token is not set correctly
|
|
28
|
+
|
|
29
|
+
# exception for unset user_id, this might cause removal of
|
|
30
|
+
# all users, or other unexpected behaviour
|
|
31
|
+
class ArgumentException < Descope::Exception; end
|
|
32
|
+
|
|
33
|
+
# exception for invalid token when its empty
|
|
34
|
+
class InvalidToken < Descope::Exception; end
|
|
35
|
+
class InvalidParameter < Descope::Exception; end
|
|
36
|
+
class Unauthorized < Descope::HTTPError; end
|
|
37
|
+
# exception for not found resource, you query for an
|
|
38
|
+
# non-existent resource, or wrong path
|
|
39
|
+
class NotFound < Descope::HTTPError; end
|
|
40
|
+
class MethodNotAllowed < Descope::HTTPError; end
|
|
41
|
+
# exception for unknown error
|
|
42
|
+
class Unsupported < Descope::HTTPError; end
|
|
43
|
+
# exception for server error
|
|
44
|
+
class ServerError < Descope::HTTPError; end
|
|
45
|
+
# exception for incorrect request, you've sent wrong params
|
|
46
|
+
class BadRequest < Descope::HTTPError; end
|
|
47
|
+
class AccessDenied < Descope::HTTPError; end
|
|
48
|
+
class RateLimitException < Descope::HTTPError; end
|
|
49
|
+
class RequestTimeout < Descope::HTTPError; end
|
|
50
|
+
end
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../exception'
|
|
4
|
+
|
|
5
|
+
module Descope
|
|
6
|
+
module Mixins
|
|
7
|
+
# Common values and methods
|
|
8
|
+
module Common
|
|
9
|
+
DEFAULT_BASE_URL = 'https://api.descope.com' # pragma: no cover
|
|
10
|
+
DEFAULT_TIMEOUT_SECONDS = 60
|
|
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
|
|
13
|
+
|
|
14
|
+
SESSION_COOKIE_NAME = 'DS'
|
|
15
|
+
REFRESH_SESSION_COOKIE_NAME = 'DSR'
|
|
16
|
+
|
|
17
|
+
SESSION_TOKEN_NAME = 'sessionToken'
|
|
18
|
+
REFRESH_SESSION_TOKEN_NAME = 'refreshSessionToken'
|
|
19
|
+
COOKIE_DATA_NAME = 'cookieData'
|
|
20
|
+
|
|
21
|
+
REDIRECT_LOCATION_COOKIE_NAME = 'Location'
|
|
22
|
+
|
|
23
|
+
module DeliveryMethod
|
|
24
|
+
WHATSAPP = 1
|
|
25
|
+
SMS = 2
|
|
26
|
+
EMAIL = 3
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def get_method_string(method)
|
|
30
|
+
name = {
|
|
31
|
+
DeliveryMethod::WHATSAPP => 'whatsapp',
|
|
32
|
+
DeliveryMethod::SMS => 'sms',
|
|
33
|
+
DeliveryMethod::EMAIL => 'email'
|
|
34
|
+
}[method]
|
|
35
|
+
|
|
36
|
+
raise ArgumentException, "Unknown delivery method: #{method}" if name.nil?
|
|
37
|
+
|
|
38
|
+
name
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def deep_copy(obj)
|
|
42
|
+
Marshal.load(Marshal.dump(obj))
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
module EndpointsV1
|
|
46
|
+
REFRESH_TOKEN_PATH = '/v1/auth/refresh'
|
|
47
|
+
SELECT_TENANT_PATH = '/v1/auth/tenant/select'
|
|
48
|
+
LOGOUT_PATH = '/v1/auth/logout'
|
|
49
|
+
LOGOUT_ALL_PATH = '/v1/auth/logoutall'
|
|
50
|
+
VALIDATE_SESSION_PATH = '/v1/auth/validate'
|
|
51
|
+
ME_PATH = '/v1/auth/me'
|
|
52
|
+
|
|
53
|
+
# accesskey
|
|
54
|
+
EXCHANGE_AUTH_ACCESS_KEY_PATH = '/v1/auth/accesskey/exchange'
|
|
55
|
+
|
|
56
|
+
# otp
|
|
57
|
+
SIGN_UP_AUTH_OTP_PATH = '/v1/auth/otp/signup'
|
|
58
|
+
SIGN_IN_AUTH_OTP_PATH = '/v1/auth/otp/signin'
|
|
59
|
+
SIGN_UP_OR_IN_AUTH_OTP_PATH = '/v1/auth/otp/signup-in'
|
|
60
|
+
VERIFY_CODE_AUTH_PATH = '/v1/auth/otp/verify'
|
|
61
|
+
UPDATE_USER_EMAIL_OTP_PATH = '/v1/auth/otp/update/email'
|
|
62
|
+
UPDATE_USER_PHONE_OTP_PATH = '/v1/auth/otp/update/phone'
|
|
63
|
+
|
|
64
|
+
# magiclink
|
|
65
|
+
SIGN_UP_AUTH_MAGICLINK_PATH = '/v1/auth/magiclink/signup'
|
|
66
|
+
SIGN_IN_AUTH_MAGICLINK_PATH = '/v1/auth/magiclink/signin'
|
|
67
|
+
SIGN_UP_OR_IN_AUTH_MAGICLINK_PATH = '/v1/auth/magiclink/signup-in'
|
|
68
|
+
VERIFY_MAGICLINK_AUTH_PATH = '/v1/auth/magiclink/verify'
|
|
69
|
+
GET_SESSION_MAGICLINK_AUTH_PATH = '/v1/auth/magiclink/pending-session'
|
|
70
|
+
UPDATE_USER_EMAIL_MAGICLINK_PATH = '/v1/auth/magiclink/update/email'
|
|
71
|
+
UPDATE_USER_PHONE_MAGICLINK_PATH = '/v1/auth/magiclink/update/phone'
|
|
72
|
+
|
|
73
|
+
# enchantedlink
|
|
74
|
+
SIGN_UP_AUTH_ENCHANTEDLINK_PATH = '/v1/auth/enchantedlink/signup'
|
|
75
|
+
SIGN_IN_AUTH_ENCHANTEDLINK_PATH = '/v1/auth/enchantedlink/signin'
|
|
76
|
+
SIGN_UP_OR_IN_AUTH_ENCHANTEDLINK_PATH = '/v1/auth/enchantedlink/signup-in'
|
|
77
|
+
VERIFY_ENCHANTEDLINK_AUTH_PATH = '/v1/auth/enchantedlink/verify'
|
|
78
|
+
GET_SESSION_ENCHANTEDLINK_AUTH_PATH = '/v1/auth/enchantedlink/pending-session'
|
|
79
|
+
UPDATE_USER_EMAIL_ENCHANTEDLINK_PATH = '/v1/auth/enchantedlink/update/email'
|
|
80
|
+
|
|
81
|
+
# oauth
|
|
82
|
+
OAUTH_START_PATH = '/v1/auth/oauth/authorize'
|
|
83
|
+
OAUTH_EXCHANGE_TOKEN_PATH = '/v1/auth/oauth/exchange'
|
|
84
|
+
OAUTH_CREATE_REDIRECT_URL_FOR_SIGN_IN_REQUEST_PATH = 'v1/auth/oauth/authorize/signin'
|
|
85
|
+
OAUTH_CREATE_REDIRECT_URL_FOR_SIGN_UP_REQUEST_PATH = 'v1/auth/oauth/authorize/signup'
|
|
86
|
+
|
|
87
|
+
# saml
|
|
88
|
+
AUTH_SAML_START_PATH = '/v1/auth/saml/authorize'
|
|
89
|
+
SAML_EXCHANGE_TOKEN_PATH = '/v1/auth/saml/exchange'
|
|
90
|
+
|
|
91
|
+
# totp
|
|
92
|
+
SIGN_UP_AUTH_TOTP_PATH = '/v1/auth/totp/signup'
|
|
93
|
+
VERIFY_TOTP_PATH = '/v1/auth/totp/verify'
|
|
94
|
+
UPDATE_TOTP_PATH = '/v1/auth/totp/update'
|
|
95
|
+
|
|
96
|
+
# webauthn
|
|
97
|
+
SIGN_UP_AUTH_WEBAUTHN_START_PATH = '/v1/auth/webauthn/signup/start'
|
|
98
|
+
SIGN_UP_AUTH_WEBAUTHN_FINISH_PATH = '/v1/auth/webauthn/signup/finish'
|
|
99
|
+
SIGN_IN_AUTH_WEBAUTHN_START_PATH = '/v1/auth/webauthn/signin/start'
|
|
100
|
+
SIGN_IN_AUTH_WEBAUTHN_FINISH_PATH = '/v1/auth/webauthn/signin/finish'
|
|
101
|
+
SIGN_UP_OR_IN_AUTH_WEBAUTHN_START_PATH = '/v1/auth/webauthn/signup-in/start'
|
|
102
|
+
UPDATE_AUTH_WEBAUTHN_START_PATH = '/v1/auth/webauthn/update/start'
|
|
103
|
+
UPDATE_AUTH_WEBAUTHN_FINISH_PATH = '/v1/auth/webauthn/update/finish'
|
|
104
|
+
|
|
105
|
+
# password
|
|
106
|
+
SIGN_UP_PASSWORD_PATH = '/v1/auth/password/signup'
|
|
107
|
+
SIGN_IN_PASSWORD_PATH = '/v1/auth/password/signin'
|
|
108
|
+
SEND_RESET_PASSWORD_PATH = '/v1/auth/password/reset'
|
|
109
|
+
UPDATE_PASSWORD_PATH = '/v1/auth/password/update'
|
|
110
|
+
REPLACE_PASSWORD_PATH = '/v1/auth/password/replace'
|
|
111
|
+
PASSWORD_POLICY_PATH = '/v1/auth/password/policy'
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
module EndpointsV2
|
|
115
|
+
PUBLIC_KEY_PATH = '/v2/keys'
|
|
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
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Descope
|
|
2
|
+
module Mixins
|
|
3
|
+
module Headers
|
|
4
|
+
# Descope default headers
|
|
5
|
+
def client_headers
|
|
6
|
+
{
|
|
7
|
+
'Content-Type' => 'application/json',
|
|
8
|
+
'x-descope-sdk-name': 'ruby',
|
|
9
|
+
'x-descope-sdk-ruby-version': RUBY_VERSION,
|
|
10
|
+
'x-descope-sdk-version': Descope::SDK_VERSION,
|
|
11
|
+
}
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require "addressable/uri"
|
|
3
|
+
require 'retryable'
|
|
4
|
+
require_relative '../exception'
|
|
5
|
+
|
|
6
|
+
module Descope
|
|
7
|
+
module Mixins
|
|
8
|
+
# HTTP-related methods
|
|
9
|
+
module HTTP
|
|
10
|
+
attr_accessor :headers, :base_uri, :timeout, :retry_count
|
|
11
|
+
|
|
12
|
+
DEFAULT_RETRIES = 3
|
|
13
|
+
MAX_ALLOWED_RETRIES = 10
|
|
14
|
+
MAX_REQUEST_RETRY_JITTER = 250
|
|
15
|
+
MAX_REQUEST_RETRY_DELAY = 1000
|
|
16
|
+
MIN_REQUEST_RETRY_DELAY = 250
|
|
17
|
+
BASE_DELAY = 100
|
|
18
|
+
|
|
19
|
+
%i[get post post_file post_form put patch delete delete_with_body].each do |method|
|
|
20
|
+
define_method(method) do |uri, body = {}, extra_headers = {}, pswd = nil|
|
|
21
|
+
body = body.delete_if { |_, v| v.nil? }
|
|
22
|
+
authorization_header(pswd) # This will set the pswd if provided, else default to the @default_pswd
|
|
23
|
+
|
|
24
|
+
@logger.debug "request => method: #{method}, uri: #{uri}, body: #{body}, extra_headers: #{extra_headers}}"
|
|
25
|
+
request_with_retry(method, uri, body, extra_headers)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def retry_options
|
|
30
|
+
sleep_timer = lambda do |attempt|
|
|
31
|
+
wait = BASE_DELAY * (2**attempt - 1) # Exponential delay with each subsequent request attempt.
|
|
32
|
+
wait += rand(wait + 1..wait + MAX_REQUEST_RETRY_JITTER) # Add jitter to the delay window.
|
|
33
|
+
wait = [MAX_REQUEST_RETRY_DELAY, wait].min # Cap delay at MAX_REQUEST_RETRY_DELAY.
|
|
34
|
+
wait = [MIN_REQUEST_RETRY_DELAY, wait].max # Ensure delay is no less than MIN_REQUEST_RETRY_DELAY.
|
|
35
|
+
wait / 1000.to_f.round(2) # convert ms to seconds
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
tries = 1 + [Integer(retry_count || DEFAULT_RETRIES), MAX_ALLOWED_RETRIES].min # Cap retries at MAX_ALLOWED_RETRIES
|
|
39
|
+
|
|
40
|
+
{
|
|
41
|
+
tries: tries,
|
|
42
|
+
sleep: sleep_timer,
|
|
43
|
+
on: Descope::RateLimitException
|
|
44
|
+
}
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def safe_parse_json(body)
|
|
48
|
+
@logger.debug "response => #{JSON.parse(body.to_s)}"
|
|
49
|
+
JSON.parse(body.to_s)
|
|
50
|
+
rescue JSON::ParserError
|
|
51
|
+
body
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def encode_uri(uri)
|
|
55
|
+
encoded_uri = base_uri ? Addressable::URI.parse(uri).normalize : Addressable::URI.escape(uri)
|
|
56
|
+
@logger.debug "will call #{url(encoded_uri)}"
|
|
57
|
+
url(encoded_uri)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def url(path)
|
|
61
|
+
"#{@base_uri}#{path}"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def add_headers(h = {})
|
|
65
|
+
raise ArgumentError, 'Headers must be an object which responds to #to_hash' unless h.respond_to?(:to_hash)
|
|
66
|
+
|
|
67
|
+
@headers ||= {}
|
|
68
|
+
@headers.merge!(h.to_hash)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def request_with_retry(method, uri, body = {}, extra_headers = {}, pswd = nil)
|
|
72
|
+
Retryable.retryable(retry_options) do
|
|
73
|
+
request(method, uri, body, extra_headers)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def request(method, uri, body = {}, extra_headers = {})
|
|
78
|
+
# @headers is getting the authorization header merged in initializer.rb
|
|
79
|
+
headers_debug = @headers.dup
|
|
80
|
+
if headers_debug['Authorization']
|
|
81
|
+
headers_debug['Authorization'] = headers_debug['Authorization'].gsub(/(.{10})\z/, '***********')
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
@logger.debug "base url: #{@base_uri}"
|
|
85
|
+
@logger.debug "request method: #{method}, uri: #{uri}, body: #{body}, extra_headers: #{extra_headers}, headers: #{headers_debug}"
|
|
86
|
+
result = case method
|
|
87
|
+
when :get
|
|
88
|
+
get_headers = @headers.merge({ params: body }).merge(extra_headers)
|
|
89
|
+
call(:get, encode_uri(uri), timeout, get_headers)
|
|
90
|
+
when :delete
|
|
91
|
+
delete_headers = @headers.merge({ params: body })
|
|
92
|
+
call(:delete, encode_uri(uri), timeout, delete_headers)
|
|
93
|
+
else
|
|
94
|
+
call(method, encode_uri(uri), timeout, @headers, body.to_json)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
raise Descope::Unsupported.new("No response from server", code: 400) unless result && result.respond_to?(:code)
|
|
98
|
+
|
|
99
|
+
@logger.info "http status code: #{result.code}"
|
|
100
|
+
case result.code
|
|
101
|
+
when 200...226 then safe_parse_json(result.body)
|
|
102
|
+
when 400 then raise Descope::BadRequest.new(result.body, code: result.code, headers: result.headers)
|
|
103
|
+
when 401 then raise Descope::Unauthorized.new(result.body, code: result.code, headers: result.headers)
|
|
104
|
+
when 403 then raise Descope::AccessDenied.new(result.body, code: result.code, headers: result.headers)
|
|
105
|
+
when 404 then raise Descope::NotFound.new(result.body, code: result.code, headers: result.headers)
|
|
106
|
+
when 405 then raise Descope::MethodNotAllowed.new(result.body, code: result.code, headers: result.headers)
|
|
107
|
+
when 429 then raise Descope::RateLimitException.new(result.body, code: result.code, headers: result.headers)
|
|
108
|
+
when 500 then raise Descope::ServerError.new(result.body, code: result.code, headers: result.headers)
|
|
109
|
+
else
|
|
110
|
+
raise Descope::Unsupported.new(result.body, code: result.code, headers: result.headers)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def call(method, url, timeout, headers, body = nil)
|
|
115
|
+
RestClient::Request.execute(
|
|
116
|
+
method:,
|
|
117
|
+
url:,
|
|
118
|
+
timeout:,
|
|
119
|
+
headers:,
|
|
120
|
+
payload: body
|
|
121
|
+
)
|
|
122
|
+
rescue RestClient::Exception => e
|
|
123
|
+
case e
|
|
124
|
+
when RestClient::RequestTimeout
|
|
125
|
+
raise Descope::RequestTimeout.new(e.message)
|
|
126
|
+
else
|
|
127
|
+
return e.response
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
|
|
5
|
+
module Descope
|
|
6
|
+
module Mixins
|
|
7
|
+
# Helper class for initializing the Descope API
|
|
8
|
+
module Initializer
|
|
9
|
+
attr_accessor :public_keys, :mlock
|
|
10
|
+
|
|
11
|
+
def initialize(config)
|
|
12
|
+
options = Hash[config.map { |(k, v)| [k.to_sym, v] }]
|
|
13
|
+
@base_uri = base_url(options)
|
|
14
|
+
@headers = client_headers
|
|
15
|
+
@project_id = options[:project_id] || ENV['DESCOPE_PROJECT_ID'] || ''
|
|
16
|
+
@public_key = options[:public_key] || ENV['DESCOPE_PUBLIC_KEY']
|
|
17
|
+
@mlock = Mutex.new
|
|
18
|
+
log_level = options[:log_level] || ENV['DESCOPE_LOG_LEVEL'] || 'info'
|
|
19
|
+
@logger ||= Descope::Mixins::Logging.logger_for(self.class.name, log_level)
|
|
20
|
+
|
|
21
|
+
@logger.debug("Initializing Descope API with project_id: #{@project_id} and base_uri: #{@base_uri}")
|
|
22
|
+
|
|
23
|
+
if @public_key.nil?
|
|
24
|
+
@public_keys = {}
|
|
25
|
+
else
|
|
26
|
+
kid, pub_key, alg = validate_and_load_public_key(@public_key)
|
|
27
|
+
@public_keys = { kid => [pub_key, alg] }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
@skip_verify = options[:skip_verify]
|
|
31
|
+
@secure = !@skip_verify
|
|
32
|
+
@management_key = options[:management_key] || ENV['DESCOPE_MANAGEMENT_KEY']
|
|
33
|
+
@logger.debug("Management Key ID: #{@management_key}")
|
|
34
|
+
@timeout_seconds = options[:timeout_seconds] || Common::DEFAULT_TIMEOUT_SECONDS
|
|
35
|
+
@jwt_validation_leeway = options[:jwt_validation_leeway] || Common::DEFAULT_JWT_VALIDATION_LEEWAY
|
|
36
|
+
|
|
37
|
+
if @project_id.to_s.empty?
|
|
38
|
+
raise AuthException.new(
|
|
39
|
+
'Unable to init Auth object because project_id cannot be empty. '\
|
|
40
|
+
'Set environment variable DESCOPE_PROJECT_ID or pass your Project ID to the init function.',
|
|
41
|
+
code: 400
|
|
42
|
+
)
|
|
43
|
+
else
|
|
44
|
+
initialize_api(options)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def self.included(klass)
|
|
49
|
+
klass.send :prepend, Initializer
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def base_url(options)
|
|
53
|
+
url = options[:descope_base_uri] || ENV['DESCOPE_BASE_URI'] || Common::DEFAULT_BASE_URL
|
|
54
|
+
return url if url.start_with? 'http'
|
|
55
|
+
|
|
56
|
+
raise AuthException.new('base url must start with http or https', code: 400)
|
|
57
|
+
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def authorization_header(pswd = nil)
|
|
61
|
+
pswd = @default_pswd if pswd.nil? || pswd.empty?
|
|
62
|
+
bearer = "#{@project_id}:#{pswd}"
|
|
63
|
+
add_headers('Authorization' => "Bearer #{bearer}")
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def initialize_api(options)
|
|
67
|
+
initialize_v1(options)
|
|
68
|
+
@default_pswd = options.fetch(:management_key, ENV['DESCOPE_MANAGEMENT_KEY'])
|
|
69
|
+
authorization_header
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def initialize_v1(_options)
|
|
73
|
+
extend Descope::Api::V1
|
|
74
|
+
extend Descope::Api::V1::Management
|
|
75
|
+
extend Descope::Api::V1::Auth
|
|
76
|
+
extend Descope::Api::V1::Session
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Descope
|
|
4
|
+
module Mixins
|
|
5
|
+
# Module to provide logger.
|
|
6
|
+
module Logging
|
|
7
|
+
|
|
8
|
+
def logger
|
|
9
|
+
# This is the magical bit that gets mixed into the other modules
|
|
10
|
+
@logger ||= Logging.logger_for(self.class.name, 'info')
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Use a hash class-ivar to cache a unique Logger per class:
|
|
14
|
+
@loggers = {}
|
|
15
|
+
|
|
16
|
+
class << self
|
|
17
|
+
def logger_for(classname, level)
|
|
18
|
+
@loggers[classname] ||= configure_logger_for(classname, level)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def configure_logger_for(classname, level = 'info')
|
|
22
|
+
logger = Logger.new(STDOUT)
|
|
23
|
+
logger.level = Object.const_get("Logger::#{level.upcase}")
|
|
24
|
+
logger.progname = classname
|
|
25
|
+
logger
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Descope
|
|
4
|
+
module Mixins
|
|
5
|
+
# Module to provide validation for specific data structures.
|
|
6
|
+
module Validation
|
|
7
|
+
def validate_tenants(key_tenants)
|
|
8
|
+
raise ArgumentError, 'key_tenants should be an Array of hashes' unless key_tenants.is_a? Array
|
|
9
|
+
|
|
10
|
+
key_tenants.each do |tenant|
|
|
11
|
+
unless tenant.is_a? Hash
|
|
12
|
+
raise ArgumentError,
|
|
13
|
+
'Each tenant should be a Hash of tenant_id and optional role_names array'
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
tenant_symbolized = tenant.transform_keys(&:to_sym)
|
|
17
|
+
|
|
18
|
+
raise ArgumentError, "Missing tenant_id key in tenant: #{tenant}" unless tenant_symbolized.key?(:tenant_id)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def validate_login_id(login_id)
|
|
23
|
+
raise AuthException, 'login_id cannot be empty' unless login_id.is_a?(String) && !login_id.empty?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def validate_user_id(user_id)
|
|
27
|
+
raise Descope::ArgumentException, 'Missing user id' if user_id.nil? || user_id.to_s.empty?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def validate_password(password)
|
|
31
|
+
raise AuthException, 'password cannot be empty' unless password.is_a?(String) && !password.empty?
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def validate_email(email)
|
|
35
|
+
raise AuthException.new('email cannot be empty', code: 400) unless email.is_a?(String) && !email.empty?
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def validate_token_not_empty(token)
|
|
39
|
+
raise AuthException.new('Token cannot be empty', code: 400) unless token.is_a?(String) && !token.empty?
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def validate_refresh_token_not_nil(refresh_token)
|
|
43
|
+
return unless refresh_token.nil? || refresh_token.empty?
|
|
44
|
+
|
|
45
|
+
raise AuthException.new('Refresh token is required to refresh a session', code: 400)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def validate_phone(method, phone)
|
|
49
|
+
raise AuthException.new('Phone number cannot be empty', code: 400) unless phone.is_a?(String) && !phone.empty?
|
|
50
|
+
raise AuthException.new('Invalid phone number', code: 400) unless phone.match?(PHONE_REGEX)
|
|
51
|
+
raise AuthException.new('Invalid delivery method', code: 400) unless [
|
|
52
|
+
DeliveryMethod::WHATSAPP, DeliveryMethod::SMS
|
|
53
|
+
].include?(method)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def verify_provider(oauth_provider)
|
|
57
|
+
return false if oauth_provider.to_s.empty? || oauth_provider.nil?
|
|
58
|
+
|
|
59
|
+
true
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def validate_tenant(tenant)
|
|
63
|
+
raise AuthException.new('Tenant cannot be empty', code: 400) unless tenant.is_a?(String) && !tenant.empty?
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def validate_redirect_url(return_url)
|
|
67
|
+
raise AuthException.new('Return_url cannot be empty', code: 400) unless return_url.is_a?(String) && !return_url.empty?
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def validate_code(code)
|
|
71
|
+
raise AuthException.new('Code cannot be empty', code: 400) unless code.is_a?(String) && !code.empty?
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def validate_scim_group_id(group_id)
|
|
75
|
+
raise AuthException.new('SCIM Group ID cannot be empty', code: 400) unless group_id.is_a?(String) && !group_id.empty?
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require 'rest-client'
|
|
2
|
+
require 'uri'
|
|
3
|
+
require 'logger'
|
|
4
|
+
require 'jwt'
|
|
5
|
+
require 'descope/mixins/headers'
|
|
6
|
+
require 'descope/mixins/http'
|
|
7
|
+
require 'descope/mixins/initializer'
|
|
8
|
+
require 'descope/mixins/validation'
|
|
9
|
+
require 'descope/mixins/logging'
|
|
10
|
+
require 'descope/mixins/common'
|
|
11
|
+
require 'descope/api/v1'
|
|
12
|
+
|
|
13
|
+
module Descope
|
|
14
|
+
# Collecting dependencies here
|
|
15
|
+
module Mixins
|
|
16
|
+
include Descope::Mixins::Common
|
|
17
|
+
include Descope::Mixins::Headers
|
|
18
|
+
include Descope::Mixins::HTTP
|
|
19
|
+
include Descope::Mixins::Initializer
|
|
20
|
+
include Descope::Mixins::Logging
|
|
21
|
+
end
|
|
22
|
+
end
|
data/lib/descope.rb
ADDED