auth0 5.9.0 → 5.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +3 -3
  3. data/.devcontainer/Dockerfile +19 -0
  4. data/.devcontainer/devcontainer.json +37 -0
  5. data/CHANGELOG.md +22 -0
  6. data/DEVELOPMENT.md +35 -0
  7. data/EXAMPLES.md +220 -0
  8. data/Gemfile +1 -0
  9. data/Gemfile.lock +59 -63
  10. data/README.md +68 -253
  11. data/auth0.gemspec +2 -4
  12. data/examples/ruby-api/Gemfile +3 -2
  13. data/examples/ruby-api/Gemfile.lock +33 -0
  14. data/examples/ruby-api/README.md +2 -2
  15. data/examples/ruby-on-rails-api/README.md +0 -2
  16. data/lib/auth0/api/authentication_endpoints.rb +70 -13
  17. data/lib/auth0/api/v2/clients.rb +42 -0
  18. data/lib/auth0/api/v2/jobs.rb +15 -4
  19. data/lib/auth0/api/v2/organizations.rb +1 -1
  20. data/lib/auth0/client_assertion.rb +45 -0
  21. data/lib/auth0/mixins/httpproxy.rb +11 -12
  22. data/lib/auth0/mixins/initializer.rb +2 -0
  23. data/lib/auth0/mixins/token_management.rb +2 -2
  24. data/lib/auth0/version.rb +1 -1
  25. data/opslevel.yml +5 -0
  26. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Clients/_patch_client/should_update_the_client_with_the_correct_attributes.yml +2 -1
  27. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Connections/_update_connection/should_update_the_connection.yml +1 -1
  28. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Rules/_update_rule/should_update_the_disabled_rule_to_be_enabled.yml +1 -1
  29. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Tenants/_update_tenant_settings/should_revert_the_tenant_name.yml +1 -1
  30. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Tenants/_update_tenant_settings/should_update_the_tenant_settings_with_a_new_tenant_name.yml +1 -1
  31. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_add_user_roles/should_add_a_Role_to_a_User_successfully.yml +1 -1
  32. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_patch_user/should_patch_the_User_successfully.yml +1 -1
  33. data/spec/lib/auth0/api/authentication_endpoints_spec.rb +632 -0
  34. data/spec/lib/auth0/api/v2/clients_spec.rb +51 -0
  35. data/spec/lib/auth0/api/v2/jobs_spec.rb +18 -0
  36. data/spec/lib/auth0/mixins/httpproxy_spec.rb +53 -17
  37. data/spec/lib/auth0/mixins/initializer_spec.rb +79 -25
  38. data/spec/lib/auth0/mixins/token_management_spec.rb +47 -37
  39. data/spec/spec_helper.rb +4 -1
  40. data/spec/support/dummy_class_for_tokens.rb +2 -0
  41. metadata +15 -35
@@ -73,12 +73,54 @@ module Auth0
73
73
  patch(path, options)
74
74
  end
75
75
 
76
+ # Creates credentials for a client
77
+ # @param client_id [string] The Id of the client to update
78
+ # @param options [hash] The payload to send to the endpoint
79
+ def create_client_credentials(client_id, options)
80
+ raise Auth0::MissingClientId, 'Must specify a client id' if client_id.to_s.empty?
81
+ raise Auth0::MissingParameter, 'Must specify a valid body' if options.to_s.empty?
82
+ post(client_credentials_path(client_id), options)
83
+ end
84
+
85
+ # Gets the credentials for a client
86
+ # @param client_id [string] The Id of the client
87
+ # @return [hash] The client credentials
88
+ def client_credentials(client_id)
89
+ raise Auth0::MissingClientId, 'Must specify a client id' if client_id.to_s.empty?
90
+ get(client_credentials_path(client_id))
91
+ end
92
+ alias get_client_credentials client_credentials
93
+
94
+ # Gets a client credential by ID
95
+ # @param client_id [string] The Id of the client
96
+ # @param credential_id [string] The Id of the credential to retrieve
97
+ # @return [hash] The credential
98
+ def client_credential(client_id, credential_id)
99
+ raise Auth0::MissingClientId, 'Must specify a client id' if client_id.to_s.empty?
100
+ raise Auth0::MissingParameter, 'Must specify a credential id' if credential_id.to_s.empty?
101
+ get("#{client_credentials_path(client_id)}/#{credential_id}")
102
+ end
103
+ alias get_client_credential client_credential
104
+
105
+ # Deletes a credential from the specified client
106
+ # @param client_id [string] The Id of the client
107
+ # @param credential_id [string] The Id of the credential to delete
108
+ def delete_client_credential(client_id, credential_id)
109
+ raise Auth0::MissingClientId, 'Must specify a client id' if client_id.to_s.empty?
110
+ raise Auth0::MissingParameter, 'Must specify a credential id' if credential_id.to_s.empty?
111
+ delete("#{client_credentials_path(client_id)}/#{credential_id}")
112
+ end
113
+
76
114
  private
77
115
 
78
116
  # Clients API path
79
117
  def clients_path
80
118
  @clients_path ||= '/api/v2/clients'
81
119
  end
120
+
121
+ def client_credentials_path(client_id)
122
+ "#{clients_path}/#{client_id}/credentials"
123
+ end
82
124
  end
83
125
  end
84
126
  end
@@ -60,6 +60,9 @@ module Auth0
60
60
  # :format [string] The format of the file. Valid values are: "json" and "csv".
61
61
  # :limit [integer] Limit the number of users to export.
62
62
  # :fields [array] A list of fields to be included in the CSV.
63
+ # This can either be an array of strings representing field names, or an object.
64
+ # If it's a string, it is mapped to the correct { name: '<field name>' } object required by the endpoint.
65
+ # If it's an object, it is passed through as-is to the endpoint.
63
66
  # If omitted, a set of predefined fields will be exported.
64
67
  #
65
68
  # @return [json] Returns the job status and properties.
@@ -109,14 +112,22 @@ module Auth0
109
112
  @jobs_path ||= '/api/v2/jobs'
110
113
  end
111
114
 
112
- # Map array of field names for export to array of objects
113
- # @param fields [array] Field names to be included in the export
114
-
115
+ # Map array of fields for export to array of objects
116
+ # @param fields [array] Fields to be included in the export
117
+ # This can either be an array of strings representing field names, or an object.
118
+ # If it's a string, it is mapped to the correct { name: '<field name>' } object required by the endpoint.
119
+ # If it's an object, it is passed through as-is to the endpoint.
115
120
  # @return [array] Returns the fields mapped as array of objects for the export_users endpoint
116
121
  def fields_for_export(fields)
117
122
  return nil if fields.to_s.empty?
118
123
 
119
- fields.map { |field| { name: field } }
124
+ fields.map { |field|
125
+ if field.is_a? String
126
+ { name: field }
127
+ else
128
+ field
129
+ end
130
+ }
120
131
  end
121
132
  end
122
133
  end
@@ -9,7 +9,7 @@ module Auth0
9
9
 
10
10
  # Get all organizations.
11
11
  # @see https://auth0.com/docs/api/management/v2/#!/Organizations/get_organizations
12
- # @param options [hash] The Hash options used to define the paging of rersults
12
+ # @param options [hash] The Hash options used to define the paging of results
13
13
  # * :per_page [integer] The amount of entries per page. Default: 50. Max value: 100.
14
14
  # * :page [integer] The page number. Zero based.
15
15
  # * :from [string] For checkpoint pagination, the ID from which to start selection from.
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'jwt'
4
+
5
+ module Auth0
6
+ module ClientAssertion
7
+ CLIENT_ASSERTION_TYPE = 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'.freeze
8
+
9
+ # Adds keys into the supplied hash for either the client secret, or client assertion. If `client_assertion_signing_key` is not nil,
10
+ # it takes precedence over `client_secret`.
11
+ # @param [hash] The hash to add the keys to
12
+ # @param client_id [string] The client ID
13
+ # @param client_secret [string] The client secret
14
+ # @param client_assertion_signing_key [PKey] The key used to sign the client assertion JWT
15
+ # @param client_assertion_signing_alg [string] The algorithm used when signing the client assertion JWT
16
+ def populate_client_assertion_or_secret(hash,
17
+ domain: @domain,
18
+ client_id: @client_id,
19
+ client_secret: @client_secret,
20
+ client_assertion_signing_key: @client_assertion_signing_key,
21
+ client_assertion_signing_alg: @client_assertion_signing_alg)
22
+
23
+ if !client_assertion_signing_key.nil?
24
+ # Create JWT
25
+ now = Time.now.to_i
26
+
27
+ payload = {
28
+ iss: client_id,
29
+ sub: client_id,
30
+ aud: "https://#{domain}/",
31
+ iat: now,
32
+ exp: now + 180,
33
+ jti: SecureRandom.uuid
34
+ }
35
+
36
+ jwt = JWT.encode payload, client_assertion_signing_key, client_assertion_signing_alg
37
+
38
+ hash[:client_assertion] = jwt
39
+ hash[:client_assertion_type] = Auth0::ClientAssertion::CLIENT_ASSERTION_TYPE
40
+ else
41
+ hash[:client_secret] = client_secret
42
+ end
43
+ end
44
+ end
45
+ end
@@ -8,11 +8,12 @@ module Auth0
8
8
  # for now, if you want to feel free to use your own http client
9
9
  module HTTPProxy
10
10
  attr_accessor :headers, :base_uri, :timeout, :retry_count
11
- DEAFULT_RETRIES = 3
11
+ DEFAULT_RETRIES = 3
12
12
  MAX_ALLOWED_RETRIES = 10
13
13
  MAX_REQUEST_RETRY_JITTER = 250
14
14
  MAX_REQUEST_RETRY_DELAY = 1000
15
- MIN_REQUEST_RETRY_DELAY = 100
15
+ MIN_REQUEST_RETRY_DELAY = 250
16
+ BASE_DELAY = 100
16
17
 
17
18
  # proxying requests from instance methods to HTTP class methods
18
19
  %i(get post post_file put patch delete delete_with_body).each do |method|
@@ -26,14 +27,14 @@ module Auth0
26
27
 
27
28
  def retry_options
28
29
  sleep_timer = lambda do |attempt|
29
- wait = 1000 * 2**attempt # Exponential delay with each subsequent request attempt.
30
- wait += rand(wait..wait+MAX_REQUEST_RETRY_JITTER) # Add jitter to the delay window.
30
+ wait = BASE_DELAY * (2**attempt-1) # Exponential delay with each subsequent request attempt.
31
+ wait += rand(wait+1..wait+MAX_REQUEST_RETRY_JITTER) # Add jitter to the delay window.
31
32
  wait = [MAX_REQUEST_RETRY_DELAY, wait].min # Cap delay at MAX_REQUEST_RETRY_DELAY.
32
33
  wait = [MIN_REQUEST_RETRY_DELAY, wait].max # Ensure delay is no less than MIN_REQUEST_RETRY_DELAY.
33
34
  wait / 1000.to_f.round(2) # convert ms to seconds
34
35
  end
35
36
 
36
- tries = 1 + [Integer(retry_count || DEAFULT_RETRIES), MAX_ALLOWED_RETRIES].min # Cap retries at MAX_ALLOWED_RETRIES
37
+ tries = 1 + [Integer(retry_count || DEFAULT_RETRIES), MAX_ALLOWED_RETRIES].min # Cap retries at MAX_ALLOWED_RETRIES
37
38
 
38
39
  {
39
40
  tries: tries,
@@ -72,15 +73,13 @@ module Auth0
72
73
 
73
74
  def request(method, uri, body = {}, extra_headers = {})
74
75
  result = if method == :get
75
- # Mutate the headers property to add parameters.
76
- add_headers({params: body})
77
- # Merge custom headers into existing ones for this req.
78
- # This prevents future calls from using them.
79
- get_headers = headers.merge extra_headers
80
- # Make the call with extra_headers, if provided.
76
+ @headers ||= {}
77
+ get_headers = @headers.merge({params: body}).merge(extra_headers)
81
78
  call(:get, encode_uri(uri), timeout, get_headers)
82
79
  elsif method == :delete
83
- call(:delete, encode_uri(uri), timeout, add_headers({params: body}))
80
+ @headers ||= {}
81
+ delete_headers = @headers.merge({ params: body })
82
+ call(:delete, encode_uri(uri), timeout, delete_headers)
84
83
  elsif method == :delete_with_body
85
84
  call(:delete, encode_uri(uri), timeout, headers, body.to_json)
86
85
  elsif method == :post_file
@@ -16,6 +16,8 @@ module Auth0
16
16
  @headers = client_headers
17
17
  @timeout = options[:timeout] || 10
18
18
  @retry_count = options[:retry_count]
19
+ @client_assertion_signing_key = options[:client_assertion_signing_key]
20
+ @client_assertion_signing_alg = options[:client_assertion_signing_alg] || 'RS256';
19
21
  extend Auth0::Api::AuthenticationEndpoints
20
22
  @client_id = options[:client_id]
21
23
  @client_secret = options[:client_secret]
@@ -6,7 +6,6 @@ module Auth0
6
6
 
7
7
  def initialize_token(options)
8
8
  @token = options[:access_token] || options[:token]
9
-
10
9
  # default expiry to an hour if a token was given but no expires_at
11
10
  @token_expires_at = @token ? options[:token_expires_at] || Time.now.to_i + 3600 : nil
12
11
 
@@ -15,9 +14,10 @@ module Auth0
15
14
  end
16
15
 
17
16
  def get_token
17
+ # pp @token_expires_at
18
18
  has_expired = @token && @token_expires_at ? @token_expires_at < (Time.now.to_i + 10) : false
19
19
 
20
- if (@token.nil? || has_expired) && @client_id && @client_secret
20
+ if (@token.nil? || has_expired) && @client_id && (@client_secret || @client_assertion_signing_key)
21
21
  response = api_token(audience: @audience)
22
22
  @token = response.token
23
23
  @token_expires_at = response.expires_in ? Time.now.to_i + response.expires_in : nil
data/lib/auth0/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  # current version of gem
2
2
  module Auth0
3
- VERSION = '5.9.0'.freeze
3
+ VERSION = '5.11.0'.freeze
4
4
  end
data/opslevel.yml ADDED
@@ -0,0 +1,5 @@
1
+ ---
2
+ version: 1
3
+ repository:
4
+ owner: dx_sdks
5
+ tags:
@@ -2,7 +2,7 @@
2
2
  http_interactions:
3
3
  - request:
4
4
  method: patch
5
- uri: https://auth0-sdk-tests.auth0.com/api/v2/clients/SftKo9ySyHnMPezQUFd0C70GBoNFM21F?fields=jwt_configuration&include_fields=false
5
+ uri: https://auth0-sdk-tests.auth0.com/api/v2/clients/SftKo9ySyHnMPezQUFd0C70GBoNFM21F
6
6
  body:
7
7
  encoding: UTF-8
8
8
  string: '{"custom_login_page_on":false,"sso":true}'
@@ -12,6 +12,7 @@ http_interactions:
12
12
  User-Agent:
13
13
  - rest-client/2.1.0 (darwin19.6.0 x86_64) ruby/2.7.0p0
14
14
  Content-Type:
15
+
15
16
  - application/json
16
17
  Auth0-Client:
17
18
  - eyJuYW1lIjoicnVieS1hdXRoMCIsInZlcnNpb24iOiI1LjUuMCIsImVudiI6eyJydWJ5IjoiMi43LjAifX0=
@@ -2,7 +2,7 @@
2
2
  http_interactions:
3
3
  - request:
4
4
  method: patch
5
- uri: https://auth0-sdk-tests.auth0.com/api/v2/connections/con_WltM0fv20JCnxOuY?email=rubytest-210908-rubytest-210908-username@auth0.com
5
+ uri: https://auth0-sdk-tests.auth0.com/api/v2/connections/con_WltM0fv20JCnxOuY
6
6
  body:
7
7
  encoding: UTF-8
8
8
  string: '{"options":{"mfa":{"active":true,"return_enroll_settings":true},"passwordPolicy":"excellent","strategy_version":2,"brute_force_protection":true}}'
@@ -2,7 +2,7 @@
2
2
  http_interactions:
3
3
  - request:
4
4
  method: patch
5
- uri: https://auth0-sdk-tests.auth0.com/api/v2/rules/rul_bsg64xEPZz4WOkXz?fields=stage&include_fields=false
5
+ uri: https://auth0-sdk-tests.auth0.com/api/v2/rules/rul_bsg64xEPZz4WOkXz
6
6
  body:
7
7
  encoding: UTF-8
8
8
  string: '{"enabled":true}'
@@ -2,7 +2,7 @@
2
2
  http_interactions:
3
3
  - request:
4
4
  method: patch
5
- uri: https://auth0-sdk-tests.auth0.com/api/v2/tenants/settings?fields=support_email&include_fields=true
5
+ uri: https://auth0-sdk-tests.auth0.com/api/v2/tenants/settings
6
6
  body:
7
7
  encoding: UTF-8
8
8
  string: '{"friendly_name":"Auth0"}'
@@ -2,7 +2,7 @@
2
2
  http_interactions:
3
3
  - request:
4
4
  method: patch
5
- uri: https://auth0-sdk-tests.auth0.com/api/v2/tenants/settings?fields=support_email&include_fields=true
5
+ uri: https://auth0-sdk-tests.auth0.com/api/v2/tenants/settings
6
6
  body:
7
7
  encoding: UTF-8
8
8
  string: '{"friendly_name":"Auth0-CHANGED"}'
@@ -2,7 +2,7 @@
2
2
  http_interactions:
3
3
  - request:
4
4
  method: post
5
- uri: https://auth0-sdk-tests.auth0.com/api/v2/users/auth0%7C613282adac819400692c0dd9/roles?per_page=2
5
+ uri: https://auth0-sdk-tests.auth0.com/api/v2/users/auth0%7C613282adac819400692c0dd9/roles
6
6
  body:
7
7
  encoding: UTF-8
8
8
  string: '{"roles":["rol_2VZOCes8HgBar3Tp"]}'
@@ -2,7 +2,7 @@
2
2
  http_interactions:
3
3
  - request:
4
4
  method: patch
5
- uri: https://auth0-sdk-tests.auth0.com/api/v2/users/auth0%7C613282adac819400692c0dd9?fields=email&include_fields=true
5
+ uri: https://auth0-sdk-tests.auth0.com/api/v2/users/auth0%7C613282adac819400692c0dd9
6
6
  body:
7
7
  encoding: UTF-8
8
8
  string: '{"email_verified":true,"user_metadata":{"addresses":{"home_address":"742