auth0 4.11.0 → 5.20.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 (358) hide show
  1. checksums.yaml +4 -4
  2. data/.version +1 -0
  3. data/CHANGELOG.md +477 -19
  4. data/README.md +80 -129
  5. data/auth0.gemspec +10 -13
  6. data/lib/auth0/algorithm.rb +5 -0
  7. data/lib/auth0/api/authentication_endpoints.rb +189 -242
  8. data/lib/auth0/api/v2/actions.rb +209 -0
  9. data/lib/auth0/api/v2/attack_protection.rb +79 -0
  10. data/lib/auth0/api/v2/branding.rb +65 -0
  11. data/lib/auth0/api/v2/client_grants.rb +27 -2
  12. data/lib/auth0/api/v2/clients.rb +42 -0
  13. data/lib/auth0/api/v2/connections.rb +14 -5
  14. data/lib/auth0/api/v2/device_credentials.rb +12 -7
  15. data/lib/auth0/api/v2/grants.rb +49 -0
  16. data/lib/auth0/api/v2/jobs.rb +28 -5
  17. data/lib/auth0/api/v2/log_streams.rb +78 -0
  18. data/lib/auth0/api/v2/logs.rb +2 -2
  19. data/lib/auth0/api/v2/organizations.rb +407 -0
  20. data/lib/auth0/api/v2/prompts.rb +70 -0
  21. data/lib/auth0/api/v2/refresh_tokens.rb +34 -0
  22. data/lib/auth0/api/v2/roles.rb +7 -3
  23. data/lib/auth0/api/v2/sessions.rb +43 -0
  24. data/lib/auth0/api/v2/tickets.rb +25 -2
  25. data/lib/auth0/api/v2/users.rb +216 -14
  26. data/lib/auth0/api/v2.rb +21 -3
  27. data/lib/auth0/client_assertion.rb +45 -0
  28. data/lib/auth0/exception.rb +19 -8
  29. data/lib/auth0/mixins/access_token_struct.rb +2 -2
  30. data/lib/auth0/mixins/api_token_struct.rb +2 -3
  31. data/lib/auth0/mixins/httpproxy.rb +99 -35
  32. data/lib/auth0/mixins/initializer.rb +6 -9
  33. data/lib/auth0/mixins/permission_struct.rb +2 -2
  34. data/lib/auth0/mixins/token_management.rb +35 -0
  35. data/lib/auth0/mixins/validation.rb +335 -1
  36. data/lib/auth0/mixins.rb +2 -1
  37. data/lib/auth0/version.rb +1 -1
  38. data/lib/auth0.rb +1 -0
  39. metadata +71 -608
  40. data/.bundle/config +0 -4
  41. data/.circleci/config.yml +0 -23
  42. data/.env.example +0 -2
  43. data/.gemrelease +0 -2
  44. data/.github/CODEOWNERS +0 -1
  45. data/.github/ISSUE_TEMPLATE.md +0 -39
  46. data/.github/PULL_REQUEST_TEMPLATE.md +0 -35
  47. data/.github/stale.yml +0 -20
  48. data/.gitignore +0 -14
  49. data/.rspec +0 -3
  50. data/.rubocop.yml +0 -10
  51. data/.rubocop_todo.yml +0 -5
  52. data/CODE_OF_CONDUCT.md +0 -3
  53. data/DEPLOYMENT.md +0 -61
  54. data/Dockerfile +0 -5
  55. data/Gemfile +0 -19
  56. data/Gemfile.lock +0 -224
  57. data/Guardfile +0 -37
  58. data/RUBYGEM.md +0 -9
  59. data/Rakefile +0 -53
  60. data/codecov.yml +0 -22
  61. data/deploy_documentation.sh +0 -29
  62. data/doc_config/templates/default/fulldoc/html/css/full_list.css +0 -79
  63. data/doc_config/templates/default/fulldoc/html/css/style.css +0 -546
  64. data/doc_config/templates/default/layout/html/breadcrumb.erb +0 -11
  65. data/doc_config/templates/default/layout/html/footer.erb +0 -115
  66. data/doc_config/templates/default/layout/html/headers.erb +0 -17
  67. data/doc_config/templates/default/layout/html/layout.erb +0 -27
  68. data/examples/ruby-api/.env.example +0 -2
  69. data/examples/ruby-api/.gitignore +0 -86
  70. data/examples/ruby-api/Gemfile +0 -8
  71. data/examples/ruby-api/README.md +0 -24
  72. data/examples/ruby-api/config.ru +0 -2
  73. data/examples/ruby-api/main.rb +0 -33
  74. data/examples/ruby-on-rails-api/.env.example +0 -2
  75. data/examples/ruby-on-rails-api/.gitignore +0 -18
  76. data/examples/ruby-on-rails-api/Gemfile +0 -48
  77. data/examples/ruby-on-rails-api/README.md +0 -27
  78. data/examples/ruby-on-rails-api/Rakefile +0 -6
  79. data/examples/ruby-on-rails-api/app/assets/images/.keep +0 -0
  80. data/examples/ruby-on-rails-api/app/assets/javascripts/application.js +0 -16
  81. data/examples/ruby-on-rails-api/app/assets/stylesheets/application.css +0 -15
  82. data/examples/ruby-on-rails-api/app/controllers/application_controller.rb +0 -8
  83. data/examples/ruby-on-rails-api/app/controllers/concerns/.keep +0 -0
  84. data/examples/ruby-on-rails-api/app/controllers/ping_controller.rb +0 -6
  85. data/examples/ruby-on-rails-api/app/controllers/secured_ping_controller.rb +0 -11
  86. data/examples/ruby-on-rails-api/app/helpers/application_helper.rb +0 -3
  87. data/examples/ruby-on-rails-api/app/mailers/.keep +0 -0
  88. data/examples/ruby-on-rails-api/app/models/.keep +0 -0
  89. data/examples/ruby-on-rails-api/app/models/User.rb +0 -5
  90. data/examples/ruby-on-rails-api/app/models/concerns/.keep +0 -0
  91. data/examples/ruby-on-rails-api/app/views/layouts/application.html.erb +0 -14
  92. data/examples/ruby-on-rails-api/bin/bundle +0 -3
  93. data/examples/ruby-on-rails-api/bin/rails +0 -4
  94. data/examples/ruby-on-rails-api/bin/rake +0 -4
  95. data/examples/ruby-on-rails-api/bin/setup +0 -29
  96. data/examples/ruby-on-rails-api/config/application.rb +0 -23
  97. data/examples/ruby-on-rails-api/config/boot.rb +0 -4
  98. data/examples/ruby-on-rails-api/config/database.yml +0 -27
  99. data/examples/ruby-on-rails-api/config/environment.rb +0 -5
  100. data/examples/ruby-on-rails-api/config/environments/development.rb +0 -37
  101. data/examples/ruby-on-rails-api/config/environments/production.rb +0 -83
  102. data/examples/ruby-on-rails-api/config/environments/test.rb +0 -41
  103. data/examples/ruby-on-rails-api/config/initializers/backtrace_silencers.rb +0 -7
  104. data/examples/ruby-on-rails-api/config/initializers/cookies_serializer.rb +0 -3
  105. data/examples/ruby-on-rails-api/config/initializers/dotenv.rb +0 -4
  106. data/examples/ruby-on-rails-api/config/initializers/filter_parameter_logging.rb +0 -4
  107. data/examples/ruby-on-rails-api/config/initializers/inflections.rb +0 -16
  108. data/examples/ruby-on-rails-api/config/initializers/knock.rb +0 -35
  109. data/examples/ruby-on-rails-api/config/initializers/mime_types.rb +0 -4
  110. data/examples/ruby-on-rails-api/config/initializers/session_store.rb +0 -3
  111. data/examples/ruby-on-rails-api/config/initializers/wrap_parameters.rb +0 -14
  112. data/examples/ruby-on-rails-api/config/locales/en.yml +0 -23
  113. data/examples/ruby-on-rails-api/config/routes.rb +0 -58
  114. data/examples/ruby-on-rails-api/config/secrets.yml +0 -28
  115. data/examples/ruby-on-rails-api/config.ru +0 -4
  116. data/examples/ruby-on-rails-api/db/schema.rb +0 -15
  117. data/examples/ruby-on-rails-api/db/seeds.rb +0 -7
  118. data/examples/ruby-on-rails-api/lib/assets/.keep +0 -0
  119. data/examples/ruby-on-rails-api/lib/tasks/.keep +0 -0
  120. data/examples/ruby-on-rails-api/log/.keep +0 -0
  121. data/examples/ruby-on-rails-api/public/404.html +0 -67
  122. data/examples/ruby-on-rails-api/public/422.html +0 -67
  123. data/examples/ruby-on-rails-api/public/500.html +0 -66
  124. data/examples/ruby-on-rails-api/public/favicon.ico +0 -0
  125. data/examples/ruby-on-rails-api/public/robots.txt +0 -5
  126. data/examples/ruby-on-rails-api/test/controllers/.keep +0 -0
  127. data/examples/ruby-on-rails-api/test/fixtures/.keep +0 -0
  128. data/examples/ruby-on-rails-api/test/helpers/.keep +0 -0
  129. data/examples/ruby-on-rails-api/test/integration/.keep +0 -0
  130. data/examples/ruby-on-rails-api/test/mailers/.keep +0 -0
  131. data/examples/ruby-on-rails-api/test/models/.keep +0 -0
  132. data/examples/ruby-on-rails-api/test/ping_controller_test.rb +0 -8
  133. data/examples/ruby-on-rails-api/test/secured_ping_controller_test.rb +0 -26
  134. data/examples/ruby-on-rails-api/test/test_helper.rb +0 -16
  135. data/lib/auth0/api/v1/clients.rb +0 -48
  136. data/lib/auth0/api/v1/connections.rb +0 -53
  137. data/lib/auth0/api/v1/logs.rb +0 -34
  138. data/lib/auth0/api/v1/rules.rb +0 -45
  139. data/lib/auth0/api/v1/users.rb +0 -164
  140. data/lib/auth0/api/v1.rb +0 -19
  141. data/publish_rubygem.sh +0 -10
  142. data/spec/fixtures/vcr_cassettes/Auth0_Api_AuthenticationEndpoints/_change_password/should_trigger_a_password_reset.yml +0 -63
  143. data/spec/fixtures/vcr_cassettes/Auth0_Api_AuthenticationEndpoints/_login_with_resource_owner/should_fail_with_an_incorrect_email.yml +0 -54
  144. data/spec/fixtures/vcr_cassettes/Auth0_Api_AuthenticationEndpoints/_login_with_resource_owner/should_fail_with_an_incorrect_password.yml +0 -54
  145. data/spec/fixtures/vcr_cassettes/Auth0_Api_AuthenticationEndpoints/_login_with_resource_owner/should_fail_with_an_invalid_audience.yml +0 -55
  146. data/spec/fixtures/vcr_cassettes/Auth0_Api_AuthenticationEndpoints/_login_with_resource_owner/should_login_successfully_with_a_custom_audience.yml +0 -117
  147. data/spec/fixtures/vcr_cassettes/Auth0_Api_AuthenticationEndpoints/_login_with_resource_owner/should_login_successfully_with_a_default_scope.yml +0 -119
  148. data/spec/fixtures/vcr_cassettes/Auth0_Api_AuthenticationEndpoints/_saml_metadata/should_retrieve_SAML_metadata.yml +0 -57
  149. data/spec/fixtures/vcr_cassettes/Auth0_Api_AuthenticationEndpoints/_userinfo/should_fail_as_not_authorized.yml +0 -55
  150. data/spec/fixtures/vcr_cassettes/Auth0_Api_AuthenticationEndpoints/_userinfo/should_return_the_userinfo.yml +0 -118
  151. data/spec/fixtures/vcr_cassettes/Auth0_Api_AuthenticationEndpoints/_wsfed_metadata/should_retrieve_WSFED_metadata.yml +0 -55
  152. data/spec/fixtures/vcr_cassettes/Auth0_Api_AuthenticationEndpoints/create_test_user.yml +0 -58
  153. data/spec/fixtures/vcr_cassettes/Auth0_Api_AuthenticationEndpoints/delete_test_user.yml +0 -54
  154. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Anomaly/_check_if_ip_is_blocked/should_return_200_response_code.yml +0 -65
  155. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Anomaly/_remove_ip_block/should_remove_an_IP_successfully.yml +0 -60
  156. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Blacklists/_add_token_to_blacklist/should_add_a_token_to_the_blacklist.yml +0 -56
  157. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Blacklists/_blacklisted_tokens/should_get_the_added_token_from_the_blacklist.yml +0 -59
  158. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_ClientGrants/_client_grants/should_return_at_least_1_result.yml +0 -62
  159. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_ClientGrants/_client_grants/should_return_the_first_page_of_one_result.yml +0 -66
  160. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_ClientGrants/_client_grants/should_return_the_test_client_grant.yml +0 -62
  161. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_ClientGrants/_delete_client_grant/should_delete_the_test_client_grant.yml +0 -54
  162. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_ClientGrants/_patch_client_grant/should_update_the_test_client_grant.yml +0 -64
  163. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_ClientGrants/create_test_client.yml +0 -118
  164. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_ClientGrants/create_test_client_grant.yml +0 -64
  165. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_ClientGrants/delete_test_client.yml +0 -54
  166. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_ClientGrants/delete_test_client_grant.yml +0 -54
  167. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Clients/_client/_filters/should_exclude_and_include_fields_properly.yml +0 -91
  168. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Clients/_client/_filters/should_include_the_specified_fields.yml +0 -63
  169. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Clients/_client/should_get_the_test_client.yml +0 -92
  170. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Clients/_clients/_filters/should_exclude_fields_not_specified.yml +0 -60
  171. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Clients/_clients/_filters/should_exclude_the_specified_fields.yml +0 -132
  172. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Clients/_clients/_filters/should_include_the_specified_fields.yml +0 -63
  173. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Clients/_clients/_filters/should_paginate_results.yml +0 -65
  174. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Clients/_clients/should_get_at_least_one_client.yml +0 -132
  175. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Clients/_delete_client/should_delete_the_test_client_without_an_error.yml +0 -54
  176. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Clients/_patch_client/should_update_the_client_with_the_correct_attributes.yml +0 -94
  177. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Clients/create_test_client.yml +0 -118
  178. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Connections/_connection/_filters/should_exclude_the_fields_indicated.yml +0 -63
  179. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Connections/_connection/_filters/should_include_the_fields_indicated.yml +0 -61
  180. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Connections/_connection/should_find_the_correct_connection.yml +0 -63
  181. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Connections/_connections/_filters/should_include_previously-created_connection_when_filtered.yml +0 -59
  182. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Connections/_connections/_filters/should_should_exclude_the_fields_indicated_from_filtered_results.yml +0 -59
  183. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Connections/_connections/_filters/should_should_include_the_fields_indicated_from_filtered_results.yml +0 -59
  184. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Connections/_connections/should_include_the_previously_created_connection.yml +0 -59
  185. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Connections/_connections/should_not_be_empty.yml +0 -59
  186. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Connections/_delete_connection/should_delete_the_connection.yml +0 -54
  187. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Connections/_delete_connection_user/should_delete_the_user_created.yml +0 -110
  188. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Connections/_update_connection/should_update_the_connection.yml +0 -66
  189. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Connections/create_test_connection.yml +0 -65
  190. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Connections/create_test_user.yml +0 -68
  191. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_DeviceCredentials/_delete_device_credential/should_delete_the_test_credential_without_an_error.yml +0 -54
  192. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_DeviceCredentials/_device_credentials/_filter_by_type/should_exclude_the_test_credential.yml +0 -59
  193. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_DeviceCredentials/_device_credentials/should_have_at_least_1_entry.yml +0 -62
  194. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_DeviceCredentials/_device_credentials/should_include_the_test_credential.yml +0 -62
  195. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_DeviceCredentials/create_test_credential.yml +0 -62
  196. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_DeviceCredentials/create_test_user.yml +0 -68
  197. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_DeviceCredentials/delete_test_credential.yml +0 -54
  198. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_DeviceCredentials/delete_test_user.yml +0 -54
  199. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Emails/_configure_provider/should_configure_a_new_email_provider.yml +0 -63
  200. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Emails/_delete_provider/should_delete_the_existing_email_provider_without_an_error.yml +0 -54
  201. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Emails/_delete_provider/should_throw_an_error_trying_to_get_the_email_provider.yml +0 -51
  202. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Emails/_get_provider/_filters/should_get_the_existing_email_provider_with_specific_fields.yml +0 -60
  203. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Emails/_get_provider/_filters/should_get_the_existing_email_provider_without_specific_fields.yml +0 -61
  204. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Emails/_get_provider/should_get_the_existing_email_provider.yml +0 -61
  205. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Emails/_update_provider/should_update_the_existing_email_provider.yml +0 -63
  206. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Emails/delete_existing_provider.yml +0 -54
  207. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Jobs/_export_users_and_get_job/should_create_an_export_users_job_successfully.yml +0 -61
  208. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Jobs/_export_users_and_get_job/should_get_the_export_users_job.yml +0 -117
  209. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Jobs/_import_users_and_get_job/should_create_an_import_users_job_successfully.yml +0 -60
  210. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Jobs/_import_users_and_get_job/should_get_the_import_users_job.yml +0 -116
  211. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Jobs/_send_verification_email_and_get_job/should_create_a_new_verification_email_job.yml +0 -119
  212. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Jobs/_send_verification_email_and_get_job/should_get_the_completed_verification_email.yml +0 -175
  213. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Jobs/_send_verification_email_and_get_job/should_reject_an_invalid_client_id.yml +0 -109
  214. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Jobs/delete_imported_user.yml +0 -110
  215. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Jobs/search_for_connection_id.yml +0 -59
  216. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Logs/_log/should_match_the_created_log_entry.yml +0 -265
  217. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Logs/_log/should_not_be_empty.yml +0 -265
  218. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Logs/_logs/_filters/should_exclude_fields_not_specified.yml +0 -61
  219. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Logs/_logs/_filters/should_exclude_the_specified_fields.yml +0 -75
  220. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Logs/_logs/_filters/should_have_one_log_entry.yml +0 -76
  221. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Logs/_logs/_filters/should_include_the_specified_fields.yml +0 -62
  222. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Logs/_logs/_from/should_take_one_log_entry.yml +0 -258
  223. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Logs/create_test_user.yml +0 -68
  224. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Logs/delete_test_disabled_rule.yml +0 -54
  225. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Logs/delete_test_enabled_rule.yml +0 -54
  226. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Logs/delete_test_user.yml +0 -54
  227. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_ResourceServers/_delete_resource_server/should_delete_the_test_server_without_an_error.yml +0 -54
  228. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_ResourceServers/_patch_resource_server/should_update_the_resource_server_with_the_correct_attributes.yml +0 -61
  229. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_ResourceServers/_resource_server/should_get_the_test_server.yml +0 -59
  230. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_ResourceServers/_resource_servers/should_get_the_test_server.yml +0 -59
  231. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_ResourceServers/_resource_servers/should_return_at_least_1_result.yml +0 -59
  232. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_ResourceServers/_resource_servers/should_return_the_first_page_of_one_result.yml +0 -64
  233. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_ResourceServers/create_test_server.yml +0 -61
  234. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_ResourceServers/delete_test_server.yml +0 -54
  235. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Roles/_add_role_permissions/should_add_a_Permission_to_the_Role_successfully.yml +0 -69
  236. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Roles/_add_role_users/should_add_a_User_to_the_Role_successfully.yml +0 -69
  237. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Roles/_delete_role/should_delete_the_Role_successfully.yml +0 -62
  238. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Roles/_get_role/should_get_the_Role_successfully.yml +0 -67
  239. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Roles/_get_role_permissions/should_get_exactly_1_Permission.yml +0 -67
  240. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Roles/_get_role_permissions/should_get_the_added_Permission_from_the_Role_successfully.yml +0 -67
  241. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Roles/_get_role_users/should_get_exactly_1_User.yml +0 -67
  242. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Roles/_get_role_users/should_get_the_added_User_from_the_Role_successfully.yml +0 -67
  243. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Roles/_get_roles/should_get_the_Role_successfully.yml +0 -67
  244. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Roles/_remove_role_permissions/should_remove_a_Permission_from_the_Role_successfully.yml +0 -64
  245. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Roles/_update_role/should_update_the_Role_successfully.yml +0 -69
  246. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Roles/create_test_api.yml +0 -69
  247. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Roles/create_test_role.yml +0 -69
  248. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Roles/create_test_user.yml +0 -69
  249. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Roles/delete_test_api.yml +0 -62
  250. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Roles/delete_test_user.yml +0 -62
  251. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Rules/_delete_rule/should_delete_the_test_disabled_rule_without_an_error.yml +0 -54
  252. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Rules/_delete_rule/should_delete_the_test_enabled_rule_without_an_error.yml +0 -54
  253. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Rules/_rule/_filters/should_exclude_the_fields_not_specified.yml +0 -62
  254. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Rules/_rule/_filters/should_exclude_the_specified_fields.yml +0 -62
  255. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Rules/_rule/_filters/should_include_the_specified_fields.yml +0 -61
  256. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Rules/_rule/should_get_a_specific_rule.yml +0 -62
  257. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Rules/_rules/_filters/should_exclude_fields_not_specified.yml +0 -60
  258. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Rules/_rules/_filters/should_include_the_specified_fields.yml +0 -61
  259. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Rules/_rules/_filters/should_return_at_least_1_disabled_rule.yml +0 -63
  260. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Rules/_rules/_filters/should_return_at_least_1_enabled_rule.yml +0 -62
  261. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Rules/_rules/_filters/should_return_paginated_results.yml +0 -128
  262. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Rules/_rules/should_return_at_least_1_rule.yml +0 -64
  263. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Rules/_update_rule/should_update_the_disabled_rule_to_be_enabled.yml +0 -64
  264. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Rules/create_test_disabled_rule.yml +0 -65
  265. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Rules/create_test_enabled_rule.yml +0 -65
  266. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Stats/_active_users/should_have_at_least_one_active_user.yml +0 -59
  267. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Stats/_daily_stats/should_have_at_least_one_stats_entry_for_the_timeframe.yml +0 -63
  268. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Tenants/_get_tenant_settings/should_get_the_tenant_settings.yml +0 -95
  269. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Tenants/_get_tenant_settings_with_specific_fields/should_exclude_a_field_not_requested.yml +0 -61
  270. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Tenants/_get_tenant_settings_with_specific_fields/should_include_the_field_requested.yml +0 -61
  271. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Tenants/_update_tenant_settings/should_revert_the_tenant_name.yml +0 -96
  272. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Tenants/_update_tenant_settings/should_update_the_tenant_settings_with_a_new_tenant_name.yml +0 -96
  273. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Tickets/_post_email_verification/should_create_an_email_verification_ticket.yml +0 -63
  274. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Tickets/_post_password_change/should_create_a_password_change_ticket.yml +0 -63
  275. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Tickets/create_test_user.yml +0 -68
  276. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Tickets/delete_test_user.yml +0 -54
  277. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_add_user_permissions/should_add_a_Permissions_for_a_User_successfully.yml +0 -67
  278. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_add_user_roles/should_add_a_Role_to_a_User_successfully.yml +0 -62
  279. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_delete_user/should_delete_the_User_successfully.yml +0 -60
  280. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_delete_user/should_delete_the_secondary_User_successfully.yml +0 -60
  281. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_delete_user_provider/should_attempt_to_delete_the_MFA_provider_for_the_User.yml +0 -60
  282. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_get_enrollments/should_get_Enrollments_for_a_User_successfully.yml +0 -65
  283. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_get_user_permissions/should_get_exactly_1_Permission_for_a_User_successfully.yml +0 -65
  284. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_get_user_permissions/should_get_the_correct_Permission_for_a_User_successfully.yml +0 -65
  285. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_get_user_roles/should_get_Roles_for_a_User_successfully.yml +0 -65
  286. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_invalidate_browsers/should_invalidate_MFA_browsers_for_the_User_successfully.yml +0 -62
  287. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_link_user_account/should_link_two_Users_successfully.yml +0 -67
  288. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_patch_user/should_patch_the_User_successfully.yml +0 -68
  289. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_remove_user_permissions/should_remove_a_Permission_from_a_User_successfully.yml +0 -62
  290. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_remove_user_roles/should_remove_a_Role_from_a_User_successfully.yml +0 -62
  291. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_unlink_user_account/should_unlink_two_Users_successfully.yml +0 -65
  292. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_user/_filters/should_exclude_fields_not_indicated.yml +0 -65
  293. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_user/_filters/should_exclude_the_fields_indicated.yml +0 -65
  294. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_user/_filters/should_include_the_fields_indicated.yml +0 -65
  295. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_user/should_retrieve_the_created_user.yml +0 -65
  296. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_user_logs/should_get_Logs_for_a_User_successfully.yml +0 -69
  297. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_users/_filters/is_expected_to_find_a_user_with_a_v2_search_engine_query.yml +0 -65
  298. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_users/_filters/is_expected_to_find_a_user_with_a_v3_search_engine_query.yml +0 -65
  299. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_users/_filters/should_exclude_the_indicated_fields_when_paginated.yml +0 -65
  300. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_users/_filters/should_include_the_indicated_fields_when_paginated.yml +0 -65
  301. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_users/_filters/should_not_include_other_fields_when_paginated.yml +0 -65
  302. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_users/_filters/should_return_the_correct_number_of_results_when_paginated.yml +0 -65
  303. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_users/should_have_at_least_one_user.yml +0 -65
  304. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/create_secondary_test_user.yml +0 -67
  305. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/create_test_api.yml +0 -67
  306. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/create_test_role.yml +0 -67
  307. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/create_test_user.yml +0 -67
  308. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/delete_test_api.yml +0 -60
  309. data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/delete_test_role.yml +0 -60
  310. data/spec/integration/lib/auth0/api/api_authentication_spec.rb +0 -120
  311. data/spec/integration/lib/auth0/api/v2/api_anomaly_spec.rb +0 -17
  312. data/spec/integration/lib/auth0/api/v2/api_blacklist_spec.rb +0 -27
  313. data/spec/integration/lib/auth0/api/v2/api_client_grants_spec.rb +0 -75
  314. data/spec/integration/lib/auth0/api/v2/api_clients_spec.rb +0 -166
  315. data/spec/integration/lib/auth0/api/v2/api_connections_spec.rb +0 -159
  316. data/spec/integration/lib/auth0/api/v2/api_device_credentials_spec.rb +0 -128
  317. data/spec/integration/lib/auth0/api/v2/api_email_spec.rb +0 -117
  318. data/spec/integration/lib/auth0/api/v2/api_jobs_spec.rb +0 -112
  319. data/spec/integration/lib/auth0/api/v2/api_logs_spec.rb +0 -91
  320. data/spec/integration/lib/auth0/api/v2/api_resource_servers_spec.rb +0 -130
  321. data/spec/integration/lib/auth0/api/v2/api_roles_spec.rb +0 -145
  322. data/spec/integration/lib/auth0/api/v2/api_rules_spec.rb +0 -177
  323. data/spec/integration/lib/auth0/api/v2/api_stats_spec.rb +0 -22
  324. data/spec/integration/lib/auth0/api/v2/api_tenants_spec.rb +0 -59
  325. data/spec/integration/lib/auth0/api/v2/api_tickets_spec.rb +0 -53
  326. data/spec/integration/lib/auth0/api/v2/api_user_blocks_spec.rb +0 -73
  327. data/spec/integration/lib/auth0/api/v2/api_users_spec.rb +0 -300
  328. data/spec/integration/lib/auth0/auth0_client_spec.rb +0 -90
  329. data/spec/lib/auth0/api/authentication_endpoints_spec.rb +0 -678
  330. data/spec/lib/auth0/api/v2/anomaly_spec.rb +0 -26
  331. data/spec/lib/auth0/api/v2/blacklists_spec.rb +0 -25
  332. data/spec/lib/auth0/api/v2/client_grants_spec.rb +0 -76
  333. data/spec/lib/auth0/api/v2/clients_spec.rb +0 -104
  334. data/spec/lib/auth0/api/v2/connections_spec.rb +0 -140
  335. data/spec/lib/auth0/api/v2/device_credentials_spec.rb +0 -73
  336. data/spec/lib/auth0/api/v2/emails_spec.rb +0 -47
  337. data/spec/lib/auth0/api/v2/guardian_spec.rb +0 -154
  338. data/spec/lib/auth0/api/v2/jobs_spec.rb +0 -111
  339. data/spec/lib/auth0/api/v2/logs_spec.rb +0 -48
  340. data/spec/lib/auth0/api/v2/resource_servers_spec.rb +0 -86
  341. data/spec/lib/auth0/api/v2/roles_spec.rb +0 -362
  342. data/spec/lib/auth0/api/v2/rules_spec.rb +0 -95
  343. data/spec/lib/auth0/api/v2/stats_spec.rb +0 -22
  344. data/spec/lib/auth0/api/v2/tenants_spec.rb +0 -26
  345. data/spec/lib/auth0/api/v2/tickets_spec.rb +0 -46
  346. data/spec/lib/auth0/api/v2/user_blocks_spec.rb +0 -52
  347. data/spec/lib/auth0/api/v2/users_by_email_spec.rb +0 -21
  348. data/spec/lib/auth0/api/v2/users_spec.rb +0 -525
  349. data/spec/lib/auth0/client_spec.rb +0 -153
  350. data/spec/lib/auth0/mixins/httpproxy_spec.rb +0 -304
  351. data/spec/lib/auth0/mixins/initializer_spec.rb +0 -30
  352. data/spec/spec_helper.rb +0 -62
  353. data/spec/support/credentials.rb +0 -30
  354. data/spec/support/dummy_class.rb +0 -18
  355. data/spec/support/dummy_class_for_proxy.rb +0 -5
  356. data/spec/support/dummy_class_for_restclient.rb +0 -2
  357. data/spec/support/import_users.json +0 -13
  358. data/spec/support/stub_response.rb +0 -1
@@ -1,44 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'addressable/uri'
4
+ require 'retryable'
5
+ require_relative '../exception'
6
+
1
7
  module Auth0
2
8
  module Mixins
3
9
  # here's the proxy for Rest calls based on rest-client, we're building all request on that gem
4
10
  # for now, if you want to feel free to use your own http client
5
11
  module HTTPProxy
6
- attr_accessor :headers, :base_uri, :timeout
12
+ attr_accessor :headers, :base_uri, :timeout, :retry_count
13
+
14
+ DEFAULT_RETRIES = 3
15
+ MAX_ALLOWED_RETRIES = 10
16
+ MAX_REQUEST_RETRY_JITTER = 250
17
+ MAX_REQUEST_RETRY_DELAY = 1000
18
+ MIN_REQUEST_RETRY_DELAY = 250
19
+ BASE_DELAY = 100
7
20
 
8
21
  # proxying requests from instance methods to HTTP class methods
9
- %i(get post post_file put patch delete delete_with_body).each do |method|
10
- define_method(method) do |path, body = {}, extra_headers = {}|
11
- safe_path = URI.escape(path)
12
- body = body.delete_if { |_, v| v.nil? }
13
- result = if method == :get
14
- # Mutate the headers property to add parameters.
15
- add_headers({params: body})
16
- # Merge custom headers into existing ones for this req.
17
- # This prevents future calls from using them.
18
- get_headers = headers.merge extra_headers
19
- # Make the call with extra_headers, if provided.
20
- call(:get, url(safe_path), timeout, get_headers)
21
- elsif method == :delete
22
- call(:delete, url(safe_path), timeout, add_headers({params: body}))
23
- elsif method == :delete_with_body
24
- call(:delete, url(safe_path), timeout, headers, body)
25
- elsif method == :post_file
26
- body.merge!(multipart: true)
27
- call(:post, url(safe_path), timeout, headers, body)
28
- else
29
- call(method, url(safe_path), timeout, headers, body.to_json)
30
- end
31
- case result.code
32
- when 200...226 then safe_parse_json(result.body)
33
- when 400 then raise Auth0::BadRequest.new(result.body, code: result.code, headers: result.headers)
34
- when 401 then raise Auth0::Unauthorized.new(result.body, code: result.code, headers: result.headers)
35
- when 403 then raise Auth0::AccessDenied.new(result.body, code: result.code, headers: result.headers)
36
- when 404 then raise Auth0::NotFound.new(result.body, code: result.code, headers: result.headers)
37
- when 429 then raise Auth0::RateLimitEncountered.new(result.body, code: result.code, headers: result.headers)
38
- when 500 then raise Auth0::ServerError.new(result.body, code: result.code, headers: result.headers)
39
- else raise Auth0::Unsupported.new(result.body, code: result.code, headers: result.headers)
40
- end
22
+ %i[get post post_file post_form put patch delete delete_with_body].each do |method|
23
+ define_method(method) do |uri, body = {}, extra_headers = {}|
24
+ body = safe_merge_body(body, extra_headers)
25
+ token = get_token
26
+ authorization_header(token) unless token.nil?
27
+ request_with_retry(method, uri, body, extra_headers)
28
+ end
29
+ end
30
+
31
+ def retry_options
32
+ sleep_timer = lambda do |attempt|
33
+ wait = BASE_DELAY * (2**attempt - 1) # Exponential delay with each subsequent request attempt.
34
+ wait += rand(wait + 1..wait + MAX_REQUEST_RETRY_JITTER) # Add jitter to the delay window.
35
+ wait = [MAX_REQUEST_RETRY_DELAY, wait].min # Cap delay at MAX_REQUEST_RETRY_DELAY.
36
+ wait = [MIN_REQUEST_RETRY_DELAY, wait].max # Ensure delay is no less than MIN_REQUEST_RETRY_DELAY.
37
+ wait / 1000.to_f.round(2) # convert ms to seconds
41
38
  end
39
+
40
+ tries = 1 + [Integer(retry_count || DEFAULT_RETRIES), MAX_ALLOWED_RETRIES].min # Cap retries at MAX_ALLOWED_RETRIES
41
+
42
+ {
43
+ tries: tries,
44
+ sleep: sleep_timer,
45
+ on: Auth0::RateLimitEncountered
46
+ }
47
+ end
48
+
49
+ def encode_uri(uri)
50
+ # if a base_uri is set then the uri can be encoded as a path
51
+ path = base_uri ? Addressable::URI.new(path: uri).normalized_path : Addressable::URI.escape(uri)
52
+ url(path)
42
53
  end
43
54
 
44
55
  def url(path)
@@ -47,6 +58,7 @@ module Auth0
47
58
 
48
59
  def add_headers(h = {})
49
60
  raise ArgumentError, 'Headers must be an object which responds to #to_hash' unless h.respond_to?(:to_hash)
61
+
50
62
  @headers ||= {}
51
63
  @headers.merge!(h.to_hash)
52
64
  end
@@ -57,6 +69,50 @@ module Auth0
57
69
  body
58
70
  end
59
71
 
72
+ def request_with_retry(method, uri, body = {}, extra_headers = {})
73
+ Retryable.retryable(retry_options) do
74
+ request(method, uri, body, extra_headers)
75
+ end
76
+ end
77
+
78
+ def request(method, uri, body = {}, extra_headers = {})
79
+ result = case method
80
+ when :get
81
+ @headers ||= {}
82
+ get_headers = @headers.merge({ params: body }).merge(extra_headers)
83
+ call(:get, encode_uri(uri), timeout, get_headers)
84
+ when :delete
85
+ @headers ||= {}
86
+ delete_headers = @headers.merge({ params: body })
87
+ call(:delete, encode_uri(uri), timeout, delete_headers)
88
+ when :delete_with_body
89
+ call(:delete, encode_uri(uri), timeout, headers, body.to_json)
90
+ when :post_file
91
+ body.merge!(multipart: true)
92
+ # Ignore the default Content-Type headers and let the HTTP client define them
93
+ post_file_headers = headers.except('Content-Type') unless headers.nil?
94
+ # Actual call with the altered headers
95
+ call(:post, encode_uri(uri), timeout, post_file_headers, body)
96
+ when :post_form
97
+ form_post_headers = headers.except('Content-Type') unless headers.nil?
98
+ call(:post, encode_uri(uri), timeout, form_post_headers, body.compact)
99
+ else
100
+ call(method, encode_uri(uri), timeout, headers, body.to_json)
101
+ end
102
+
103
+ case result.code
104
+ when 200...226 then safe_parse_json(result.body)
105
+ when 400 then raise Auth0::BadRequest.new(result.body, code: result.code, headers: result.headers)
106
+ when 401 then raise Auth0::Unauthorized.new(result.body, code: result.code, headers: result.headers)
107
+ when 403 then raise Auth0::AccessDenied.new(result.body, code: result.code, headers: result.headers)
108
+ when 404 then raise Auth0::NotFound.new(result.body, code: result.code, headers: result.headers)
109
+ when 429 then raise Auth0::RateLimitEncountered.new(result.body, code: result.code,
110
+ headers: result.headers)
111
+ when 500 then raise Auth0::ServerError.new(result.body, code: result.code, headers: result.headers)
112
+ else raise Auth0::Unsupported.new(result.body, code: result.code, headers: result.headers)
113
+ end
114
+ end
115
+
60
116
  def call(method, url, timeout, headers, body = nil)
61
117
  RestClient::Request.execute(
62
118
  method: method,
@@ -68,11 +124,19 @@ module Auth0
68
124
  rescue RestClient::Exception => e
69
125
  case e
70
126
  when RestClient::RequestTimeout
71
- raise Auth0::RequestTimeout.new(e.message)
127
+ raise Auth0::RequestTimeout, e.message
72
128
  else
73
- return e.response
129
+ e.response
74
130
  end
75
131
  end
132
+
133
+ private
134
+
135
+ def safe_merge_body(body, extra = {})
136
+ return body unless body.is_a?(Hash)
137
+ merged = extra.any? ? body.merge(extra) : body
138
+ merged.compact
139
+ end
76
140
  end
77
141
  end
78
142
  end
@@ -15,9 +15,13 @@ module Auth0
15
15
  @base_uri = base_url(options)
16
16
  @headers = client_headers
17
17
  @timeout = options[:timeout] || 10
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';
18
21
  extend Auth0::Api::AuthenticationEndpoints
19
22
  @client_id = options[:client_id]
20
23
  @client_secret = options[:client_secret]
24
+ @organization = options[:organization]
21
25
  initialize_api(options)
22
26
  end
23
27
 
@@ -40,7 +44,7 @@ module Auth0
40
44
  private
41
45
 
42
46
  def initialize_api(options)
43
- api_v2?(options) ? initialize_v2(options) : initialize_v1
47
+ initialize_v2(options)
44
48
  raise InvalidCredentials, 'Must supply a valid API token' if @token.nil?
45
49
  if options.fetch(:authorization, nil) == 'Basic'
46
50
  authorization_header_basic(options)
@@ -57,14 +61,7 @@ module Auth0
57
61
 
58
62
  def initialize_v2(options)
59
63
  extend Auth0::Api::V2
60
- @token = options[:access_token] || options[:token]
61
- @token = api_token.token if @token.nil? && @client_id && @client_secret
62
- end
63
-
64
- def initialize_v1
65
- extend Auth0::Api::V1
66
- raise InvalidCredentials, 'Invalid API v1 client_id and client_secret' if @client_id.nil? || @client_secret.nil?
67
- @token = obtain_access_token
64
+ initialize_token(options)
68
65
  end
69
66
 
70
67
  def api_v2?(options)
@@ -1,3 +1,3 @@
1
- Permission = Struct.new :permission_name, :resource_server_identifier do
1
+ Auth0::Permission = Struct.new :permission_name, :resource_server_identifier do
2
2
 
3
- end
3
+ end
@@ -0,0 +1,35 @@
1
+ module Auth0
2
+ module Mixins
3
+ module TokenManagement
4
+
5
+ # Get the Client's api token (or generate a new one if it has expired).
6
+ #
7
+ # @note This method may perform a network request to refresh an expired token. It is not thread-safe.
8
+ # @return [String] the api token
9
+ def get_token
10
+ has_expired = @token && @token_expires_at ? @token_expires_at < (Time.now.to_i + 10) : false
11
+
12
+ if (@token.nil? || has_expired) && @client_id && (@client_secret || @client_assertion_signing_key)
13
+ response = api_token(audience: @audience)
14
+ @token = response.token
15
+ @token_expires_at = response.expires_in ? Time.now.to_i + response.expires_in : nil
16
+
17
+ @token
18
+ else
19
+ @token
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def initialize_token(options)
26
+ @token = options[:access_token] || options[:token]
27
+ # default expiry to an hour if a token was given but no expires_at
28
+ @token_expires_at = @token ? options[:token_expires_at] || Time.now.to_i + 3600 : nil
29
+
30
+ @audience = options[:api_identifier] || "https://#{@domain}/api/v2/"
31
+ get_token() if @token.nil?
32
+ end
33
+ end
34
+ end
35
+ end
@@ -1,3 +1,11 @@
1
+ require 'zache'
2
+
3
+ class Zache
4
+ def last(key)
5
+ @hash[key][:value] if @hash.key?(key)
6
+ end
7
+ end
8
+
1
9
  module Auth0
2
10
  module Mixins
3
11
  # Module to provide validation for specific data structures.
@@ -15,11 +23,337 @@ module Auth0
15
23
  raise Auth0::InvalidParameter, 'Must supply an array of Permissions' unless permissions.kind_of?(Array)
16
24
  raise Auth0::MissingParameter, 'Must supply an array of Permissions' if permissions.empty?
17
25
  raise Auth0::InvalidParameter, 'All array elements must be Permissions' unless permissions.all? do |permission|
18
- permission.kind_of? Permission
26
+ permission.kind_of? ::Auth0::Permission
19
27
  end
20
28
  permissions.map { |permission| permission.to_h }
21
29
  end
22
30
 
31
+ # rubocop:disable Metrics/ClassLength
32
+ class IdTokenValidator
33
+ def initialize(context)
34
+ @context = context
35
+ end
36
+
37
+ def validate(id_token)
38
+ decoding_error = 'ID token could not be decoded'
39
+
40
+ unless !id_token.to_s.empty? && id_token.split('.').count == 3
41
+ raise Auth0::InvalidIdToken, decoding_error
42
+ end
43
+
44
+ begin
45
+ header = JWT::JSON.parse(JWT::Base64.url_decode(id_token.split('.').first))
46
+ rescue
47
+ raise Auth0::InvalidIdToken, decoding_error
48
+ end
49
+
50
+ claims = decode_and_validate_signature(id_token, header)
51
+ validate_claims(claims)
52
+ end
53
+
54
+ private
55
+
56
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity
57
+ def decode_and_validate_signature(id_token, header)
58
+ algorithm = @context[:algorithm]
59
+
60
+ unless algorithm.is_a?(Auth0::Mixins::Validation::JWTAlgorithm)
61
+ raise Auth0::InvalidIdToken, "Signature algorithm of \"#{algorithm}\" is not supported"
62
+ end
63
+
64
+ # The expiration verification will be performed in the validate_claims method
65
+ options = { algorithms: [algorithm.name], verify_expiration: false, verify_not_before: false }
66
+ secret = nil
67
+
68
+ case algorithm
69
+ when Auth0::Algorithm::RS256
70
+ kid = header['kid']
71
+ jwks = JSON.parse(JSON[algorithm.jwks], symbolize_names: true)
72
+
73
+ if !jwks[:keys].find { |key| key[:kid] == kid } && !algorithm.fetched_jwks?
74
+ jwks = JSON.parse(JSON[algorithm.jwks(force: true)], symbolize_names: true)
75
+ end
76
+
77
+ options[:jwks] = jwks
78
+ when Auth0::Algorithm::HS256
79
+ secret = algorithm.secret
80
+ end
81
+
82
+ begin
83
+ result = JWT.decode(id_token, secret, true, options)
84
+ result.first
85
+ rescue JWT::VerificationError
86
+ raise Auth0::InvalidIdToken, 'Invalid ID token signature'
87
+ rescue JWT::IncorrectAlgorithm
88
+ alg = header['alg']
89
+ raise Auth0::InvalidIdToken, "Signature algorithm of \"#{alg}\" is not supported. Expected the ID token"\
90
+ " to be signed with \"#{algorithm.name}\""
91
+ rescue JWT::DecodeError
92
+ raise Auth0::InvalidIdToken, "Could not find a public key for Key ID (kid) \"#{kid}\""
93
+ end
94
+ end
95
+
96
+ # rubocop:disable Metrics/PerceivedComplexity
97
+ def validate_claims(claims)
98
+ leeway = @context[:leeway]
99
+ nonce = @context[:nonce]
100
+ issuer = @context[:issuer]
101
+ audience = @context[:audience]
102
+ max_age = @context[:max_age]
103
+ org = @context[:organization]
104
+
105
+ raise Auth0::InvalidParameter, 'Must supply a valid leeway' unless leeway.is_a?(Integer) && leeway >= 0
106
+ raise Auth0::InvalidParameter, 'Must supply a valid nonce' unless nonce.nil? || !nonce.to_s.empty?
107
+ raise Auth0::InvalidParameter, 'Must supply a valid issuer' unless issuer.nil? || !issuer.to_s.empty?
108
+ raise Auth0::InvalidParameter, 'Must supply a valid audience' unless audience.nil? || !audience.to_s.empty?
109
+ raise Auth0::InvalidParameter, 'Must supply a valid organization' unless org.nil? || !org.to_s.empty?
110
+
111
+ unless max_age.nil? || (max_age.is_a?(Integer) && max_age >= 0)
112
+ raise Auth0::InvalidParameter, 'Must supply a valid max_age'
113
+ end
114
+
115
+ validate_iss(claims, issuer)
116
+ validate_sub(claims)
117
+ validate_aud(claims, audience)
118
+ validate_exp(claims, leeway)
119
+ validate_iat(claims, leeway)
120
+ validate_nonce(claims, nonce) if nonce
121
+ validate_azp(claims, audience) if claims['aud'].is_a?(Array) && claims['aud'].count > 1
122
+ validate_auth_time(claims, max_age, leeway) if max_age
123
+ validate_org(claims, org) if org
124
+ end
125
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
126
+
127
+ def validate_iss(claims, expected)
128
+ unless claims.key?('iss') && claims['iss'].is_a?(String)
129
+ raise Auth0::InvalidIdToken, 'Issuer (iss) claim must be a string present in the ID token'
130
+ end
131
+
132
+ unless expected == claims['iss']
133
+ raise Auth0::InvalidIdToken, "Issuer (iss) claim mismatch in the ID token; expected \"#{expected}\","\
134
+ " found \"#{claims['iss']}\""
135
+ end
136
+ end
137
+
138
+ def validate_sub(claims)
139
+ unless claims.key?('sub') && claims['sub'].is_a?(String)
140
+ raise Auth0::InvalidIdToken, 'Subject (sub) claim must be a string present in the ID token'
141
+ end
142
+ end
143
+
144
+ def validate_aud(claims, expected)
145
+ unless claims.key?('aud') && (claims['aud'].is_a?(String) || claims['aud'].is_a?(Array))
146
+ raise Auth0::InvalidIdToken, 'Audience (aud) claim must be a string or array of strings present'\
147
+ ' in the ID token'
148
+ end
149
+
150
+ if claims['aud'].is_a?(String) && expected != claims['aud']
151
+ raise Auth0::InvalidIdToken, "Audience (aud) claim mismatch in the ID token; expected \"#{expected}\","\
152
+ " found \"#{claims['aud']}\""
153
+ elsif claims['aud'].is_a?(Array) && !claims['aud'].include?(expected)
154
+ raise Auth0::InvalidIdToken, "Audience (aud) claim mismatch in the ID token; expected \"#{expected}\""\
155
+ " but was not one of \"#{claims['aud'].join ', '}\""
156
+ end
157
+ end
158
+
159
+ def validate_exp(claims, leeway)
160
+ unless claims.key?('exp') && claims['exp'].is_a?(Integer)
161
+ raise Auth0::InvalidIdToken, 'Expiration Time (exp) claim must be a number present in the ID token'
162
+ end
163
+
164
+ now = @context[:clock] || Time.now.to_i
165
+ exp_time = claims['exp'] + leeway
166
+
167
+ unless now < exp_time
168
+ raise Auth0::InvalidIdToken, 'Expiration Time (exp) claim mismatch in the ID token; current time'\
169
+ " \"#{now}\" is after expiration time \"#{exp_time}\""
170
+ end
171
+ end
172
+
173
+ def validate_iat(claims, leeway)
174
+ unless claims.key?('iat') && claims['iat'].is_a?(Integer)
175
+ raise Auth0::InvalidIdToken, 'Issued At (iat) claim must be a number present in the ID token'
176
+ end
177
+ end
178
+
179
+ def validate_nonce(claims, expected)
180
+ unless claims.key?('nonce') && claims['nonce'].is_a?(String)
181
+ raise Auth0::InvalidIdToken, 'Nonce (nonce) claim must be a string present in the ID token'
182
+ end
183
+
184
+ unless expected == claims['nonce']
185
+ raise Auth0::InvalidIdToken, "Nonce (nonce) claim mismatch in the ID token; expected \"#{expected}\","\
186
+ " found \"#{claims['nonce']}\""
187
+ end
188
+ end
189
+
190
+ def validate_org(claims, expected)
191
+ validate_as_id = expected.start_with? 'org_'
192
+
193
+ if validate_as_id
194
+ unless claims.key?('org_id') && claims['org_id'].is_a?(String)
195
+ raise Auth0::InvalidIdToken, 'Organization Id (org_id) claim must be a string present in the ID token'
196
+ end
197
+
198
+ unless expected == claims['org_id']
199
+ raise Auth0::InvalidIdToken, "Organization Id (org_id) claim value mismatch in the ID token; expected \"#{expected}\","\
200
+ " found \"#{claims['org_id']}\""
201
+ end
202
+ else
203
+ unless claims.key?('org_name') && claims['org_name'].is_a?(String)
204
+ raise Auth0::InvalidIdToken, 'Organization Name (org_name) claim must be a string present in the ID token'
205
+ end
206
+
207
+ unless expected.downcase == claims['org_name']
208
+ raise Auth0::InvalidIdToken, "Organization Name (org_name) claim value mismatch in the ID token; expected \"#{expected}\","\
209
+ " found \"#{claims['org_name']}\""
210
+ end
211
+ end
212
+ end
213
+
214
+ def validate_azp(claims, expected)
215
+ unless claims.key?('azp') && claims['azp'].is_a?(String)
216
+ raise Auth0::InvalidIdToken, 'Authorized Party (azp) claim must be a string present in the ID token'
217
+ end
218
+
219
+ unless expected == claims['azp']
220
+ raise Auth0::InvalidIdToken, 'Authorized Party (azp) claim mismatch in the ID token; expected'\
221
+ " \"#{expected}\", found \"#{claims['azp']}\""
222
+ end
223
+ end
224
+
225
+ def validate_auth_time(claims, max_age, leeway)
226
+ unless claims.key?('auth_time') && claims['auth_time'].is_a?(Integer)
227
+ raise Auth0::InvalidIdToken, 'Authentication Time (auth_time) claim must be a number present in the ID'\
228
+ ' token when Max Age (max_age) is specified'
229
+ end
230
+
231
+ now = @context[:clock] || Time.now.to_i
232
+ auth_valid_until = claims['auth_time'] + max_age + leeway
233
+
234
+ unless now < auth_valid_until
235
+ raise Auth0::InvalidIdToken, 'Authentication Time (auth_time) claim in the ID token indicates that too'\
236
+ ' much time has passed since the last end-user authentication. Current time'\
237
+ " \"#{now}\" is after last auth at \"#{auth_valid_until}\""
238
+ end
239
+ end
240
+ end
241
+ # rubocop:enable Metrics/ClassLength
242
+
243
+ class JWTAlgorithm
244
+ private_class_method :new
245
+
246
+ def name
247
+ raise RuntimeError, 'Must be overriden by the subclasses'
248
+ end
249
+ end
250
+
251
+ module Algorithm
252
+ # Represents the HS256 algorithm, which rely on shared secrets.
253
+ # @see https://auth0.com/docs/tokens/concepts/signing-algorithms
254
+ class HS256 < JWTAlgorithm
255
+ class << self
256
+ private :new
257
+
258
+ # Create a new instance passing the shared secret.
259
+ # @param secret [string] The HMAC shared secret.
260
+ # @return [HS256] A new instance.
261
+ def secret(secret)
262
+ new secret
263
+ end
264
+ end
265
+
266
+ attr_accessor :secret
267
+
268
+ def initialize(secret)
269
+ raise Auth0::InvalidParameter, 'Must supply a valid secret' if secret.to_s.empty?
270
+
271
+ @secret = secret
272
+ end
273
+
274
+ # Returns the algorithm name.
275
+ # @return [string] The algorithm name.
276
+ def name
277
+ 'HS256'
278
+ end
279
+ end
280
+
281
+ # Represents the RS256 algorithm, which rely on public key certificates.
282
+ # @see https://auth0.com/docs/tokens/concepts/signing-algorithms
283
+ class RS256 < JWTAlgorithm
284
+ include Auth0::Mixins::HTTPProxy
285
+
286
+ @@cache = Zache.new.freeze
287
+
288
+ class << self
289
+ private :new
290
+
291
+ # Create a new instance passing the JWK set url.
292
+ # @param url [string] The url where the JWK set is located.
293
+ # @param lifetime [integer] The lifetime of the JWK set in-memory cache in seconds.
294
+ # Must be a non-negative value. Defaults to *600 seconds* (10 minutes).
295
+ # @return [RS256] A new instance.
296
+ def jwks_url(url, lifetime: 10 * 60)
297
+ new url, lifetime
298
+ end
299
+
300
+ # Clear the JWK set cache.
301
+ def remove_jwks
302
+ @@cache.remove_by { true }
303
+ end
304
+ end
305
+
306
+ def initialize(jwks_url, lifetime)
307
+ raise Auth0::InvalidParameter, 'Must supply a valid jwks_url' if jwks_url.to_s.empty?
308
+ raise Auth0::InvalidParameter, 'Must supply a valid lifetime' unless lifetime.is_a?(Integer) && lifetime >= 0
309
+
310
+ @lifetime = lifetime
311
+ @jwks_url = jwks_url
312
+ @did_fetch_jwks = false
313
+ end
314
+
315
+ # Returns the algorithm name.
316
+ # @return [string] The algorithm name.
317
+ def name
318
+ 'RS256'
319
+ end
320
+
321
+ # Fetches the JWK set from the in-memory cache or from the url.
322
+ # @return [hash] A JWK set.
323
+ def jwks(force: false)
324
+ result = fetch_jwks if force
325
+
326
+ if result
327
+ @@cache.put(@jwks_url, result, lifetime: @lifetime)
328
+ return result
329
+ end
330
+
331
+ previous_value = @@cache.last(@jwks_url)
332
+
333
+ @@cache.get(@jwks_url, lifetime: @lifetime, dirty: true) do
334
+ new_value = fetch_jwks
335
+
336
+ raise Auth0::InvalidIdToken, 'Could not fetch the JWK set' unless new_value || previous_value
337
+
338
+ new_value || previous_value
339
+ end
340
+ end
341
+
342
+ # Returns whether or not the JWK set was fetched from the url.
343
+ # @return [boolean] +true+ if a request to the JWK set url was made, +false+ otherwise.
344
+ def fetched_jwks?
345
+ @did_fetch_jwks
346
+ end
347
+
348
+ private
349
+
350
+ def fetch_jwks
351
+ result = request_with_retry(:get, @jwks_url, {}, {})
352
+ @did_fetch_jwks = result.is_a?(Hash) && result.key?('keys')
353
+ result if @did_fetch_jwks
354
+ end
355
+ end
356
+ end
23
357
  end
24
358
  end
25
359
  end
data/lib/auth0/mixins.rb CHANGED
@@ -9,15 +9,16 @@ require 'auth0/mixins/httpproxy'
9
9
  require 'auth0/mixins/initializer'
10
10
  require 'auth0/mixins/permission_struct'
11
11
  require 'auth0/mixins/validation'
12
+ require 'auth0/mixins/token_management'
12
13
 
13
14
  require 'auth0/api/authentication_endpoints'
14
- require 'auth0/api/v1'
15
15
  require 'auth0/api/v2'
16
16
 
17
17
  module Auth0
18
18
  # Collecting dependencies here
19
19
  module Mixins
20
20
  include Auth0::Mixins::Headers
21
+ include Auth0::Mixins::TokenManagement
21
22
  include Auth0::Mixins::HTTPProxy
22
23
  include Auth0::Mixins::Initializer
23
24
  end
data/lib/auth0/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  # current version of gem
2
2
  module Auth0
3
- VERSION = '4.11.0'.freeze
3
+ VERSION = '5.20.0'.freeze
4
4
  end
data/lib/auth0.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'auth0/version'
2
2
  require 'auth0/mixins'
3
3
  require 'auth0/exception'
4
+ require 'auth0/algorithm'
4
5
  require 'auth0/client'
5
6
  require 'auth0_client'
6
7
  # Namespace for ruby-auth0 logic