auth0 4.7.0 → 4.12.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.
- checksums.yaml +4 -4
- data/.circleci/config.yml +23 -0
- data/.env.example +2 -0
- data/.github/CODEOWNERS +1 -0
- data/.github/stale.yml +20 -0
- data/.gitignore +0 -1
- data/.rubocop.yml +2 -0
- data/CHANGELOG.md +83 -0
- data/DEPLOYMENT.md +25 -3
- data/Gemfile +3 -1
- data/Gemfile.lock +231 -0
- data/README.md +114 -25
- data/auth0.gemspec +6 -6
- data/codecov.yml +22 -0
- data/deploy_documentation.sh +1 -1
- data/lib/auth0.rb +1 -0
- data/lib/auth0/algorithm.rb +5 -0
- data/lib/auth0/api/authentication_endpoints.rb +38 -2
- data/lib/auth0/api/v2.rb +6 -0
- data/lib/auth0/api/v2/anomaly.rb +36 -0
- data/lib/auth0/api/v2/client_grants.rb +5 -1
- data/lib/auth0/api/v2/guardian.rb +142 -0
- data/lib/auth0/api/v2/jobs.rb +22 -3
- data/lib/auth0/api/v2/roles.rb +172 -0
- data/lib/auth0/api/v2/users.rb +115 -4
- data/lib/auth0/exception.rb +35 -7
- data/lib/auth0/mixins.rb +8 -3
- data/lib/auth0/mixins/httpproxy.rb +11 -8
- data/lib/auth0/mixins/permission_struct.rb +3 -0
- data/lib/auth0/mixins/validation.rb +340 -0
- data/lib/auth0/version.rb +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Anomaly/_check_if_ip_is_blocked/should_return_200_response_code.yml +65 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Anomaly/_remove_ip_block/should_remove_an_IP_successfully.yml +60 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_ClientGrants/_client_grants/should_return_at_least_1_result.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_ClientGrants/_client_grants/should_return_the_first_page_of_one_result.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_ClientGrants/_client_grants/should_return_the_test_client_grant.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_ClientGrants/_delete_client_grant/should_delete_the_test_client_grant.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_ClientGrants/_patch_client_grant/should_update_the_test_client_grant.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_ClientGrants/create_test_client.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_ClientGrants/create_test_client_grant.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_ClientGrants/delete_test_client.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_ClientGrants/delete_test_client_grant.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Clients/_client/_filters/should_exclude_and_include_fields_properly.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Clients/_client/_filters/should_include_the_specified_fields.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Clients/_client/should_get_the_test_client.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Clients/_clients/_filters/should_exclude_fields_not_specified.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Clients/_clients/_filters/should_exclude_the_specified_fields.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Clients/_clients/_filters/should_include_the_specified_fields.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Clients/_clients/_filters/should_paginate_results.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Clients/_clients/should_get_at_least_one_client.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Clients/_delete_client/should_delete_the_test_client_without_an_error.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Clients/_patch_client/should_update_the_client_with_the_correct_attributes.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Clients/create_test_client.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Connections/_connection/_filters/should_exclude_the_fields_indicated.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Connections/_connection/_filters/should_include_the_fields_indicated.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Connections/_connection/should_find_the_correct_connection.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Connections/_connections/_filters/should_include_previously-created_connection_when_filtered.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Connections/_connections/_filters/should_should_exclude_the_fields_indicated_from_filtered_results.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Connections/_connections/_filters/should_should_include_the_fields_indicated_from_filtered_results.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Connections/_connections/should_include_the_previously_created_connection.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Connections/_connections/should_not_be_empty.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Connections/_delete_connection/should_delete_the_connection.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Connections/_delete_connection_user/should_delete_the_user_created.yml +2 -2
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Connections/_update_connection/should_update_the_connection.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Connections/create_test_connection.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Connections/create_test_user.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_DeviceCredentials/create_test_user.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_DeviceCredentials/delete_test_credential.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_DeviceCredentials/delete_test_user.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Emails/_configure_provider/should_configure_a_new_email_provider.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Emails/_delete_provider/should_delete_the_existing_email_provider_without_an_error.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Emails/_delete_provider/should_throw_an_error_trying_to_get_the_email_provider.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Emails/_get_provider/_filters/should_get_the_existing_email_provider_with_specific_fields.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Emails/_get_provider/_filters/should_get_the_existing_email_provider_without_specific_fields.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Emails/_get_provider/should_get_the_existing_email_provider.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Emails/_update_provider/should_update_the_existing_email_provider.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Emails/delete_existing_provider.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Logs/_log/should_match_the_created_log_entry.yml +2 -2
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Logs/_log/should_not_be_empty.yml +2 -2
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Logs/_logs/_filters/should_exclude_fields_not_specified.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Logs/_logs/_filters/should_exclude_the_specified_fields.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Logs/_logs/_filters/should_have_one_log_entry.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Logs/_logs/_filters/should_include_the_specified_fields.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Logs/_logs/_from/should_take_one_log_entry.yml +2 -2
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Logs/create_test_user.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Logs/delete_test_disabled_rule.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Logs/delete_test_enabled_rule.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Logs/delete_test_user.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Roles/_add_role_permissions/should_add_a_Permission_to_the_Role_successfully.yml +69 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Roles/_add_role_users/should_add_a_User_to_the_Role_successfully.yml +69 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Roles/_delete_role/should_delete_the_Role_successfully.yml +62 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Roles/_get_role/should_get_the_Role_successfully.yml +67 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Roles/_get_role_permissions/should_get_exactly_1_Permission.yml +67 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Roles/_get_role_permissions/should_get_the_added_Permission_from_the_Role_successfully.yml +67 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Roles/_get_role_users/should_get_exactly_1_User.yml +67 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Roles/_get_role_users/should_get_the_added_User_from_the_Role_successfully.yml +67 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Roles/_get_roles/should_get_the_Role_successfully.yml +67 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Roles/_remove_role_permissions/should_remove_a_Permission_from_the_Role_successfully.yml +64 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Roles/_update_role/should_update_the_Role_successfully.yml +69 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Roles/create_test_api.yml +69 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Roles/create_test_role.yml +69 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Roles/create_test_user.yml +69 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Roles/delete_test_api.yml +62 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Roles/delete_test_user.yml +62 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Rules/_delete_rule/should_delete_the_test_disabled_rule_without_an_error.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Rules/_delete_rule/should_delete_the_test_enabled_rule_without_an_error.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Rules/_rule/_filters/should_exclude_the_fields_not_specified.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Rules/_rule/_filters/should_exclude_the_specified_fields.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Rules/_rule/_filters/should_include_the_specified_fields.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Rules/_rule/should_get_a_specific_rule.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Rules/_rules/_filters/should_exclude_fields_not_specified.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Rules/_rules/_filters/should_include_the_specified_fields.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Rules/_rules/_filters/should_return_at_least_1_disabled_rule.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Rules/_rules/_filters/should_return_at_least_1_enabled_rule.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Rules/_rules/_filters/should_return_paginated_results.yml +2 -2
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Rules/_rules/should_return_at_least_1_rule.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Rules/_update_rule/should_update_the_disabled_rule_to_be_enabled.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Rules/create_test_disabled_rule.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Rules/create_test_enabled_rule.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Stats/_active_users/should_have_at_least_one_active_user.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Stats/_daily_stats/should_have_at_least_one_stats_entry_for_the_timeframe.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Tenants/_get_tenant_settings/should_get_the_tenant_settings.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Tenants/_get_tenant_settings_with_specific_fields/should_exclude_a_field_not_requested.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Tenants/_get_tenant_settings_with_specific_fields/should_include_the_field_requested.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Tenants/_update_tenant_settings/should_revert_the_tenant_name.yml +1 -1
- 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
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Tickets/_post_email_verification/should_create_an_email_verification_ticket.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Tickets/_post_password_change/should_create_a_password_change_ticket.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Tickets/create_test_user.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Tickets/delete_test_user.yml +1 -1
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_add_user_permissions/should_add_a_Permissions_for_a_User_successfully.yml +67 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_add_user_roles/should_add_a_Role_to_a_User_successfully.yml +62 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_delete_user/{should_delete_the_user_successfully.yml → should_delete_the_User_successfully.yml} +14 -8
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_delete_user/should_delete_the_secondary_User_successfully.yml +60 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_delete_user_provider/should_attempt_to_delete_the_MFA_provider_for_the_User.yml +60 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_get_enrollments/should_get_Enrollments_for_a_User_successfully.yml +65 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_get_user_permissions/should_get_exactly_1_Permission_for_a_User_successfully.yml +65 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_get_user_permissions/should_get_the_correct_Permission_for_a_User_successfully.yml +65 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_get_user_roles/should_get_Roles_for_a_User_successfully.yml +65 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_invalidate_browsers/should_invalidate_MFA_browsers_for_the_User_successfully.yml +62 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_link_user_account/should_link_two_Users_successfully.yml +67 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_patch_user/should_patch_the_User_successfully.yml +68 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_remove_user_permissions/should_remove_a_Permission_from_a_User_successfully.yml +62 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_remove_user_roles/should_remove_a_Role_from_a_User_successfully.yml +62 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_unlink_user_account/should_unlink_two_Users_successfully.yml +65 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_user/_filters/should_exclude_fields_not_indicated.yml +15 -10
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_user/_filters/should_exclude_the_fields_indicated.yml +15 -13
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_user/_filters/should_include_the_fields_indicated.yml +15 -13
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_user/should_retrieve_the_created_user.yml +15 -16
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_user_logs/should_get_Logs_for_a_User_successfully.yml +69 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_users/_filters/is_expected_to_find_a_user_with_a_v2_search_engine_query.yml +14 -8
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_users/_filters/is_expected_to_find_a_user_with_a_v3_search_engine_query.yml +14 -8
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_users/_filters/should_exclude_the_indicated_fields_when_paginated.yml +13 -7
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_users/_filters/should_include_the_indicated_fields_when_paginated.yml +13 -7
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_users/_filters/should_not_include_other_fields_when_paginated.yml +13 -7
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_users/_filters/should_return_the_correct_number_of_results_when_paginated.yml +13 -7
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_users/should_have_at_least_one_user.yml +13 -7
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/create_secondary_test_user.yml +67 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/create_test_api.yml +67 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/create_test_role.yml +67 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/create_test_user.yml +15 -16
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/delete_test_api.yml +60 -0
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/delete_test_role.yml +60 -0
- data/spec/integration/lib/auth0/api/v2/api_anomaly_spec.rb +17 -0
- data/spec/integration/lib/auth0/api/v2/api_roles_spec.rb +145 -0
- data/spec/integration/lib/auth0/api/v2/api_users_spec.rb +150 -32
- data/spec/integration/lib/auth0/auth0_client_spec.rb +3 -4
- data/spec/lib/auth0/api/authentication_endpoints_spec.rb +37 -10
- data/spec/lib/auth0/api/v2/anomaly_spec.rb +26 -0
- data/spec/lib/auth0/api/v2/client_grants_spec.rb +17 -0
- data/spec/lib/auth0/api/v2/guardian_spec.rb +154 -0
- data/spec/lib/auth0/api/v2/jobs_spec.rb +33 -1
- data/spec/lib/auth0/api/v2/roles_spec.rb +362 -0
- data/spec/lib/auth0/api/v2/users_spec.rb +406 -66
- data/spec/lib/auth0/mixins/httpproxy_spec.rb +81 -2
- data/spec/lib/auth0/mixins/validation_spec.rb +474 -0
- data/spec/spec_helper.rb +11 -7
- data/spec/support/credentials.rb +4 -13
- data/spec/support/dummy_class.rb +1 -1
- data/spec/support/stub_response.rb +1 -1
- metadata +145 -23
- data/.travis.yml +0 -18
- data/build_travis.sh +0 -7
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_patch_user/should_patch_email_verified_and_return_the_updated_data.yml +0 -68
- data/spec/fixtures/vcr_cassettes/Auth0_Api_V2_Users/_patch_user/should_patch_user_metadata_and_return_the_updated_user.yml +0 -69
@@ -66,8 +66,19 @@ describe Auth0::Mixins::HTTPProxy do
|
|
66
66
|
expect { @instance.send(http_method, '/test') }.to raise_error(Auth0::Unsupported)
|
67
67
|
end
|
68
68
|
|
69
|
+
it "should raise Auth0::RequestTimeout on send http #{http_method} method
|
70
|
+
to path defined through HTTP when RestClient::RequestTimeout received" do
|
71
|
+
allow(RestClient::Request).to receive(:execute).with(method: http_method,
|
72
|
+
url: '/test',
|
73
|
+
timeout: nil,
|
74
|
+
headers: { params: {} },
|
75
|
+
payload: nil)
|
76
|
+
.and_raise(RestClient::Exceptions::OpenTimeout.new)
|
77
|
+
expect { @instance.send(http_method, '/test') }.to raise_error(Auth0::RequestTimeout)
|
78
|
+
end
|
79
|
+
|
69
80
|
it "should raise Auth0::BadRequest on send http #{http_method} method
|
70
|
-
to path defined through HTTP when 400
|
81
|
+
to path defined through HTTP when 400 status received" do
|
71
82
|
@exception.response = StubResponse.new({}, false, 400)
|
72
83
|
allow(RestClient::Request).to receive(:execute).with(method: http_method,
|
73
84
|
url: '/test',
|
@@ -89,6 +100,35 @@ describe Auth0::Mixins::HTTPProxy do
|
|
89
100
|
.and_raise(@exception)
|
90
101
|
expect { @instance.send(http_method, '/test') }.to raise_error(Auth0::AccessDenied)
|
91
102
|
end
|
103
|
+
|
104
|
+
it "should raise Auth0::RateLimitEncountered on send http #{http_method} method
|
105
|
+
to path defined through HTTP when 429 recieved" do
|
106
|
+
headers = {
|
107
|
+
'X-RateLimit-Limit' => 10,
|
108
|
+
'X-RateLimit-Remaining' => 0,
|
109
|
+
'X-RateLimit-Reset' => 1560564149
|
110
|
+
}
|
111
|
+
@exception.response = StubResponse.new({}, false, 429, headers)
|
112
|
+
allow(RestClient::Request).to receive(:execute).with(method: http_method,
|
113
|
+
url: '/test',
|
114
|
+
timeout: nil,
|
115
|
+
headers: { params: {} },
|
116
|
+
payload: nil)
|
117
|
+
.and_raise(@exception)
|
118
|
+
expect { @instance.send(http_method, '/test') }.to raise_error { |error|
|
119
|
+
expect(error).to be_a(Auth0::RateLimitEncountered)
|
120
|
+
expect(error).to have_attributes(
|
121
|
+
error_data: {
|
122
|
+
headers: headers,
|
123
|
+
code: 429
|
124
|
+
},
|
125
|
+
headers: headers,
|
126
|
+
http_code: 429,
|
127
|
+
reset: Time.at(1560564149)
|
128
|
+
)
|
129
|
+
}
|
130
|
+
end
|
131
|
+
|
92
132
|
it "should raise Auth0::ServerError on send http #{http_method} method
|
93
133
|
to path defined through HTTP when 500 received" do
|
94
134
|
@exception.response = StubResponse.new({}, false, 500)
|
@@ -148,6 +188,34 @@ describe Auth0::Mixins::HTTPProxy do
|
|
148
188
|
.and_raise(@exception)
|
149
189
|
expect { @instance.send(http_method, '/test') }.to raise_error(Auth0::Unauthorized)
|
150
190
|
end
|
191
|
+
|
192
|
+
it "should raise Auth0::RateLimitEncountered on send http #{http_method} method
|
193
|
+
to path defined through HTTP when 429 status received" do
|
194
|
+
headers = {
|
195
|
+
'X-RateLimit-Limit' => 10,
|
196
|
+
'X-RateLimit-Remaining' => 0,
|
197
|
+
'X-RateLimit-Reset' => 1560564149
|
198
|
+
}
|
199
|
+
@exception.response = StubResponse.new({}, false, 429,headers)
|
200
|
+
allow(RestClient::Request).to receive(:execute).with(method: http_method,
|
201
|
+
url: '/test',
|
202
|
+
timeout: nil,
|
203
|
+
headers: nil,
|
204
|
+
payload: '{}')
|
205
|
+
.and_raise(@exception)
|
206
|
+
expect { @instance.send(http_method, '/test') }.to raise_error { |error|
|
207
|
+
expect(error).to be_a(Auth0::RateLimitEncountered)
|
208
|
+
expect(error).to have_attributes(
|
209
|
+
error_data: {
|
210
|
+
headers: headers,
|
211
|
+
code: 429
|
212
|
+
},
|
213
|
+
headers: headers,
|
214
|
+
http_code: 429,
|
215
|
+
reset: Time.at(1560564149)
|
216
|
+
)
|
217
|
+
}
|
218
|
+
end
|
151
219
|
|
152
220
|
it "should raise Auth0::NotFound on send http #{http_method} method
|
153
221
|
to path defined through HTTP when 404 status received" do
|
@@ -173,8 +241,19 @@ describe Auth0::Mixins::HTTPProxy do
|
|
173
241
|
expect { @instance.send(http_method, '/test') }.to raise_error(Auth0::Unsupported)
|
174
242
|
end
|
175
243
|
|
244
|
+
it "should raise Auth0::RequestTimeout on send http #{http_method} method
|
245
|
+
to path defined through HTTP when RestClient::RequestTimeout received" do
|
246
|
+
allow(RestClient::Request).to receive(:execute).with(method: http_method,
|
247
|
+
url: '/test',
|
248
|
+
timeout: nil,
|
249
|
+
headers: nil,
|
250
|
+
payload: '{}')
|
251
|
+
.and_raise(RestClient::Exceptions::OpenTimeout.new)
|
252
|
+
expect { @instance.send(http_method, '/test') }.to raise_error(Auth0::RequestTimeout)
|
253
|
+
end
|
254
|
+
|
176
255
|
it "should raise Auth0::BadRequest on send http #{http_method} method
|
177
|
-
to path defined through HTTP when 400
|
256
|
+
to path defined through HTTP when 400 status received" do
|
178
257
|
@exception.response = StubResponse.new({}, false, 400)
|
179
258
|
allow(RestClient::Request).to receive(:execute).with(method: http_method,
|
180
259
|
url: '/test',
|
@@ -0,0 +1,474 @@
|
|
1
|
+
# rubocop:disable Metrics/BlockLength
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
RSA_PUB_KEY_JWK_1 = { 'kty': "RSA", 'use': 'sig', 'n': "uGbXWiK3dQTyCbX5xdE4yCuYp0AF2d15Qq1JSXT_lx8CEcXb9RbDddl8jGDv-spi5qPa8qEHiK7FwV2KpRE983wGPnYsAm9BxLFb4YrLYcDFOIGULuk2FtrPS512Qea1bXASuvYXEpQNpGbnTGVsWXI9C-yjHztqyL2h8P6mlThPY9E9ue2fCqdgixfTFIF9Dm4SLHbphUS2iw7w1JgT69s7of9-I9l5lsJ9cozf1rxrXX4V1u_SotUuNB3Fp8oB4C1fLBEhSlMcUJirz1E8AziMCxS-VrRPDM-zfvpIJg3JljAh3PJHDiLu902v9w-Iplu1WyoB2aPfitxEhRN0Yw", 'e': 'AQAB', 'kid': 'test-key-1' }.freeze
|
5
|
+
RSA_PUB_KEY_JWK_2 = { 'kty': "RSA", 'use': 'sig', 'n': "uGbXWiK3dQTyCbX5xdE4yCuYp0AF2d15Qq1JSXT_lx8CEcXb9RbDddl8jGDv-spi5qPa8qEHiK7FwV2KpRE983wGPnYsAm9BxLFb4YrLYcDFOIGULuk2FtrPS512Qea1bXASuvYXEpQNpGbnTGVsWXI9C-yjHztqyL2h8P6mlThPY9E9ue2fCqdgixfTFIF9Dm4SLHbphUS2iw7w1JgT69s7of9-I9l5lsJ9cozf1rxrXX4V1u_SotUuNB3Fp8oB4C1fLBEhSlMcUJirz1E8AziMCxS-VrRPDM-zfvpIJg3JljAh3PJHDiLu902v9w-Iplu1WyoB2aPfitxEhRN0Yw", 'e': 'AQAB', 'kid': 'test-key-2' }.freeze
|
6
|
+
JWKS_RESPONSE_1 = { 'keys': [RSA_PUB_KEY_JWK_1] }.freeze
|
7
|
+
JWKS_RESPONSE_2 = { 'keys': [RSA_PUB_KEY_JWK_2] }.freeze
|
8
|
+
JWKS_URL = 'https://tokens-test.auth0.com/.well-known/jwks.json'.freeze
|
9
|
+
HMAC_SHARED_SECRET = 'secret'.freeze
|
10
|
+
|
11
|
+
LEEWAY = 60
|
12
|
+
CLOCK = 1587592561 # Apr 22 2020 21:56:01 UTC
|
13
|
+
CONTEXT = { algorithm: Auth0::Algorithm::HS256.secret(HMAC_SHARED_SECRET), leeway: LEEWAY, audience: 'tokens-test-123', issuer: 'https://tokens-test.auth0.com/', clock: CLOCK }.freeze
|
14
|
+
|
15
|
+
describe Auth0::Mixins::Validation::IdTokenValidator do
|
16
|
+
subject { @instance }
|
17
|
+
|
18
|
+
context 'instance' do
|
19
|
+
it 'is expected respond to :validate' do
|
20
|
+
instance = Auth0::Mixins::Validation::IdTokenValidator.new({})
|
21
|
+
|
22
|
+
expect(instance).to respond_to(:validate)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'ID token decoding' do
|
27
|
+
expected_error = 'ID token could not be decoded'
|
28
|
+
instance = Auth0::Mixins::Validation::IdTokenValidator.new({})
|
29
|
+
|
30
|
+
it 'is expected to raise an error with a nil id_token' do
|
31
|
+
expect { instance.validate(nil) }.to raise_exception(expected_error)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'is expected to raise an error with an empty id_token' do
|
35
|
+
expect { instance.validate('') }.to raise_exception(expected_error)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'is expected to raise an error with an invalid format' do
|
39
|
+
expect { instance.validate('a.b') }.to raise_exception(expected_error)
|
40
|
+
expect { instance.validate('a.b.') }.to raise_exception(expected_error)
|
41
|
+
expect { instance.validate('a.b.c.d') }.to raise_exception(expected_error)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'is expected to raise an error with an invalid encoding' do
|
45
|
+
expect { instance.validate('a.b.c') }.to raise_exception(expected_error)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'algorithm verification' do
|
50
|
+
token = 'eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc1OTI1NjEsIm5vbmNlIjoiYTFiMmMzZDRlNSIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU4NzY3ODk2MX0.Hn38QVtN_mWN0c-jOa-Fqq69kXpbBp0THsvE-CQ47Ps'
|
51
|
+
|
52
|
+
it 'is expected to raise an error with an unsupported algorithm' do
|
53
|
+
instance = Auth0::Mixins::Validation::IdTokenValidator.new({ algorithm: 'ES256' })
|
54
|
+
|
55
|
+
expect { instance.validate(token) }.to raise_exception('Signature algorithm of "ES256" is not supported')
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'is expected to raise an error when the algorithm does not match the alg header value' do
|
59
|
+
algorithm = Auth0::Algorithm::HS256.secret(HMAC_SHARED_SECRET)
|
60
|
+
instance = Auth0::Mixins::Validation::IdTokenValidator.new({ algorithm: algorithm })
|
61
|
+
|
62
|
+
expect { instance.validate(token) }.to raise_exception('Signature algorithm of "ES256" is not supported. Expected the ID token to be signed with "HS256"')
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'HS256 signature verification' do
|
67
|
+
before :each do
|
68
|
+
algorithm = Auth0::Algorithm::HS256.secret(HMAC_SHARED_SECRET)
|
69
|
+
@instance = Auth0::Mixins::Validation::IdTokenValidator.new(CONTEXT.merge({ algorithm: algorithm }))
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'is expected not to raise an error with a valid signature' do
|
73
|
+
token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc1OTI1NjEsIm5vbmNlIjoiYTFiMmMzZDRlNSIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU4NzY3ODk2MX0.Hn38QVtN_mWN0c-jOa-Fqq69kXpbBp0THsvE-CQ47Ps'
|
74
|
+
|
75
|
+
expect { @instance.validate(token) }.not_to raise_exception
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'is expected to raise an error with an invalid signature' do
|
79
|
+
token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc1OTI1NjEsIm5vbmNlIjoiYTFiMmMzZDRlNSIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU4NzY3ODk2MX0.invalidsignature'
|
80
|
+
|
81
|
+
expect { @instance.validate(token) }.to raise_exception('Invalid ID token signature')
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'RS256 signature verification' do
|
86
|
+
before :each do
|
87
|
+
stub_jwks
|
88
|
+
algorithm = Auth0::Algorithm::RS256.jwks_url(JWKS_URL)
|
89
|
+
@instance = Auth0::Mixins::Validation::IdTokenValidator.new(CONTEXT.merge({ algorithm: algorithm }))
|
90
|
+
end
|
91
|
+
|
92
|
+
after :each do
|
93
|
+
Auth0::Algorithm::RS256.remove_jwks
|
94
|
+
WebMock.reset!
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'is expected not to raise an error with a valid signature' do
|
98
|
+
token = 'eyJhbGciOiJSUzI1NiIsImtpZCI6InRlc3Qta2V5LTEifQ.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc1OTI1NjEsIm5vbmNlIjoiYTFiMmMzZDRlNSIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU4NzY3ODk2MX0.jE00ARUiAwrKEoAMwbioKYj4bUZjmg31V7McDtIPsJJ16rYcvI-e5mtSSMgCmAom6t-WA7dsSWCJUlBCW2nAMvyCZ-hj8HG9Z0RmQEiwig9Fk22avoX94zdx65TwAeDfn2uMRaX_ps3TJcn4nymUtMp8Lps_vMw15eJerKThlSO4KuLTrvDDdRaCRamAd7jxuzhiwOt0mB0TVD55b5itA02pGuyapbjQXvvLYEx8OgpyIaAkB9Ry25abgjev0bSw2kjFZckG3lv9QgvZM85m9l3Rbrc6msNPGfMDFWGyT3Tu2ObqnSEA-57hZeuCbFrOya3vUwgSlc66rfvZj2xpzg'
|
99
|
+
|
100
|
+
expect { @instance.validate(token) }.not_to raise_exception
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'is expected to raise an error with an invalid signature' do
|
104
|
+
token = 'eyJhbGciOiJSUzI1NiIsImtpZCI6InRlc3Qta2V5LTEifQ.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc1OTI1NjEsIm5vbmNlIjoiYTFiMmMzZDRlNSIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU4NzY3ODk2MX0.invalidsignature'
|
105
|
+
|
106
|
+
expect { @instance.validate(token) }.to raise_exception('Invalid ID token signature')
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'is expected to raise an error when the public key cannot be found' do
|
110
|
+
token = 'eyJhbGciOiJSUzI1NiIsImtpZCI6InRlc3Qta2V5LTIifQ.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc1OTI1NjEsIm5vbmNlIjoiYTFiMmMzZDRlNSIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU4NzY3ODk2MX0.r2ksgiiM8zGJ6byea_Fq_zWWEmUdOnwQLVdb5JzgdBv1GUQFp-4LNaRhcga4FIrbKgxaPeewGLTq2VqfjmNJUNfARcE3QEacQ_JEHkC6zKZIiqcu4msHl8X9HXyHxOPHMTTjYMjauPzET7UH_oLxF68DDDQqvKX9VqLsncpyC-KdTCFTLGlFSq6pxmYt6gwrF2Uo15Gzc6qe2I9-MTXCYd44VW1zQi6GhNJNKbXH6U3bf7nof0ot1PSjBXXuLgf6d3qumTStECCjIUmdBb6FiEX4SSRI4MgHWj4q0LyN28baRpYwYPhVnjzUxOP7OLjKiHs45ORBhuAWhrJnuR_uBQ'
|
111
|
+
|
112
|
+
expect { @instance.validate(token) }.to raise_exception('Could not find a public key for Key ID (kid) "test-key-2"')
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'is expected to fetch the JWK set from the url if the public key cannot be found and the cache is not empty' do
|
116
|
+
token = 'eyJhbGciOiJSUzI1NiIsImtpZCI6InRlc3Qta2V5LTIifQ.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc1OTI1NjEsIm5vbmNlIjoiYTFiMmMzZDRlNSIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU4NzY3ODk2MX0.r2ksgiiM8zGJ6byea_Fq_zWWEmUdOnwQLVdb5JzgdBv1GUQFp-4LNaRhcga4FIrbKgxaPeewGLTq2VqfjmNJUNfARcE3QEacQ_JEHkC6zKZIiqcu4msHl8X9HXyHxOPHMTTjYMjauPzET7UH_oLxF68DDDQqvKX9VqLsncpyC-KdTCFTLGlFSq6pxmYt6gwrF2Uo15Gzc6qe2I9-MTXCYd44VW1zQi6GhNJNKbXH6U3bf7nof0ot1PSjBXXuLgf6d3qumTStECCjIUmdBb6FiEX4SSRI4MgHWj4q0LyN28baRpYwYPhVnjzUxOP7OLjKiHs45ORBhuAWhrJnuR_uBQ'
|
117
|
+
Auth0::Algorithm::RS256.jwks_url(JWKS_URL).jwks
|
118
|
+
stub_jwks(JWKS_RESPONSE_2)
|
119
|
+
@instance.validate(token)
|
120
|
+
|
121
|
+
expect(a_request(:get, JWKS_URL)).to have_been_made.twice
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
context 'context validation' do
|
126
|
+
token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc1OTI1NjEsIm5vbmNlIjoiYTFiMmMzZDRlNSIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU4NzY3ODk2MX0.Hn38QVtN_mWN0c-jOa-Fqq69kXpbBp0THsvE-CQ47Ps'
|
127
|
+
|
128
|
+
it 'is expected to raise an error with a non-integer leeway' do
|
129
|
+
instance = Auth0::Mixins::Validation::IdTokenValidator.new(CONTEXT.merge({ leeway: '1' }))
|
130
|
+
|
131
|
+
expect { instance.validate(token) }.to raise_exception('Must supply a valid leeway')
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'is expected to raise an error with a negative leeway' do
|
135
|
+
instance = Auth0::Mixins::Validation::IdTokenValidator.new(CONTEXT.merge({ leeway: -1 }))
|
136
|
+
|
137
|
+
expect { instance.validate(token) }.to raise_exception('Must supply a valid leeway')
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'is expected to raise an error with an empty nonce' do
|
141
|
+
instance = Auth0::Mixins::Validation::IdTokenValidator.new(CONTEXT.merge({ nonce: '' }))
|
142
|
+
|
143
|
+
expect { instance.validate(token) }.to raise_exception('Must supply a valid nonce')
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'is expected to raise an error with an empty issuer' do
|
147
|
+
instance = Auth0::Mixins::Validation::IdTokenValidator.new(CONTEXT.merge({ issuer: '' }))
|
148
|
+
|
149
|
+
expect { instance.validate(token) }.to raise_exception('Must supply a valid issuer')
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'is expected to raise an error with an empty audience' do
|
153
|
+
instance = Auth0::Mixins::Validation::IdTokenValidator.new(CONTEXT.merge({ audience: '' }))
|
154
|
+
|
155
|
+
expect { instance.validate(token) }.to raise_exception('Must supply a valid audience')
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'is expected to raise an error with a non-integer max_age' do
|
159
|
+
instance = Auth0::Mixins::Validation::IdTokenValidator.new(CONTEXT.merge({ max_age: '1' }))
|
160
|
+
|
161
|
+
expect { instance.validate(token) }.to raise_exception('Must supply a valid max_age')
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'is expected to raise an error with a negative max_age' do
|
165
|
+
instance = Auth0::Mixins::Validation::IdTokenValidator.new(CONTEXT.merge({ max_age: -1 }))
|
166
|
+
|
167
|
+
expect { instance.validate(token) }.to raise_exception('Must supply a valid max_age')
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
context 'claims validation' do
|
172
|
+
before :all do
|
173
|
+
@instance = Auth0::Mixins::Validation::IdTokenValidator.new(CONTEXT)
|
174
|
+
end
|
175
|
+
|
176
|
+
it 'is expected to raise an error with a missing iss' do
|
177
|
+
token = 'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc1OTI1NjEsIm5vbmNlIjoiYTFiMmMzZDRlNSIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU4NzY3ODk2MX0.QL2B2tqJhlW9rc8HQ3PQKkjDufBeSvtRBtJmRPdQ5K8'
|
178
|
+
|
179
|
+
expect { @instance.validate(token) }.to raise_exception('Issuer (iss) claim must be a string present in the ID token')
|
180
|
+
end
|
181
|
+
|
182
|
+
it 'is expected to raise an error with a invalid iss' do
|
183
|
+
token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJzb21ldGhpbmctZWxzZSIsInN1YiI6ImF1dGgwfDEyMzQ1Njc4OSIsImF1ZCI6WyJ0b2tlbnMtdGVzdC0xMjMiLCJleHRlcm5hbC10ZXN0LTk5OSJdLCJleHAiOjE1ODc3NjUzNjEsImlhdCI6MTU4NzU5MjU2MSwibm9uY2UiOiJhMWIyYzNkNGU1IiwiYXpwIjoidG9rZW5zLXRlc3QtMTIzIiwiYXV0aF90aW1lIjoxNTg3Njc4OTYxfQ.AhMMouDlGMdxTYrY9Cn-p8svJ8ssKmsHeT6JNRVTh10'
|
184
|
+
|
185
|
+
expect { @instance.validate(token) }.to raise_exception("Issuer (iss) claim mismatch in the ID token; expected \"#{CONTEXT[:issuer]}\", found \"something-else\"")
|
186
|
+
end
|
187
|
+
|
188
|
+
it 'is expected to raise an error with a missing sub' do
|
189
|
+
token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc1OTI1NjEsIm5vbmNlIjoiYTFiMmMzZDRlNSIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU4NzY3ODk2MX0._4sUXtAZYpGrO3QaYArXnk2JivCqixa7hgHhH3w4SlY'
|
190
|
+
|
191
|
+
expect { @instance.validate(token) }.to raise_exception('Subject (sub) claim must be a string present in the ID token')
|
192
|
+
end
|
193
|
+
|
194
|
+
it 'is expected to raise an error with a missing aud' do
|
195
|
+
token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJleHAiOjE1ODc3NjUzNjEsImlhdCI6MTU4NzU5MjU2MSwibm9uY2UiOiJhMWIyYzNkNGU1IiwiYXpwIjoidG9rZW5zLXRlc3QtMTIzIiwiYXV0aF90aW1lIjoxNTg3Njc4OTYxfQ.TlwnBmGUKe0SElSYKxPqsG1mujkK2t1CwDJGGiWRdXA'
|
196
|
+
|
197
|
+
expect { @instance.validate(token) }.to raise_exception('Audience (aud) claim must be a string or array of strings present in the ID token')
|
198
|
+
end
|
199
|
+
|
200
|
+
it 'is expected to raise an error with an invalid string aud' do
|
201
|
+
token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOiJleHRlcm5hbC10ZXN0LTk5OSIsImV4cCI6MTU4Nzc2NTM2MSwiaWF0IjoxNTg3NTkyNTYxLCJub25jZSI6ImExYjJjM2Q0ZTUiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOjE1ODc2Nzg5NjF9.-Tf5CIi2bZ51UdgqxFWQNXpJJmK5GgsetcVoVrQwHIA'
|
202
|
+
|
203
|
+
expect { @instance.validate(token) }.to raise_exception("Audience (aud) claim mismatch in the ID token; expected \"#{CONTEXT[:audience]}\", found \"external-test-999\"")
|
204
|
+
end
|
205
|
+
|
206
|
+
it 'is expected to raise an error with an invalid array aud' do
|
207
|
+
token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsiZXh0ZXJuYWwtdGVzdC05OTgiLCJleHRlcm5hbC10ZXN0LTk5OSJdLCJleHAiOjE1ODc3NjUzNjEsImlhdCI6MTU4NzU5MjU2MSwibm9uY2UiOiJhMWIyYzNkNGU1IiwiYXpwIjoidG9rZW5zLXRlc3QtMTIzIiwiYXV0aF90aW1lIjoxNTg3Njc4OTYxfQ.Q1GRVL5g3RLQqG5sEV_cc8WW_oiZzFIAfzRfBdxMW2s'
|
208
|
+
|
209
|
+
expect { @instance.validate(token) }.to raise_exception("Audience (aud) claim mismatch in the ID token; expected \"#{CONTEXT[:audience]}\" but was not one of \"external-test-998, external-test-999\"")
|
210
|
+
end
|
211
|
+
|
212
|
+
it 'is expected to raise an error with a missing exp' do
|
213
|
+
token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiaWF0IjoxNTg3NTkyNTYxLCJub25jZSI6ImExYjJjM2Q0ZTUiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOjE1ODc2Nzg5NjF9.aoLiQX3sHsf1bEbc0axbjJ9qV6hhomtEzJq-FT8OGF0'
|
214
|
+
|
215
|
+
expect { @instance.validate(token) }.to raise_exception('Expiration Time (exp) claim must be a number present in the ID token')
|
216
|
+
end
|
217
|
+
|
218
|
+
it 'is expected to raise an error with a invalid exp' do
|
219
|
+
token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NTkyNTYxLCJpYXQiOjE1ODc1OTI1NjEsIm5vbmNlIjoiYTFiMmMzZDRlNSIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU4NzY3ODk2MX0.A8Pc0vlCG5Ufez7VIoRqXTYpJehalTEgGX9cR2xJLkU'
|
220
|
+
clock = CLOCK + LEEWAY + 1
|
221
|
+
instance = Auth0::Mixins::Validation::IdTokenValidator.new(CONTEXT.merge({ clock: clock }))
|
222
|
+
|
223
|
+
expect { instance.validate(token) }.to raise_exception("Expiration Time (exp) claim mismatch in the ID token; current time \"#{clock}\" is after expiration time \"1587592621\"")
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'is expected to raise an error with a missing iat' do
|
227
|
+
token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJub25jZSI6ImExYjJjM2Q0ZTUiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOjE1ODc2Nzg5NjF9.Jea6UVJsAK7Hnb494f_WIQCIbaLTnnCvMenSY1Y2toc'
|
228
|
+
|
229
|
+
expect { @instance.validate(token) }.to raise_exception('Issued At (iat) claim must be a number present in the ID token')
|
230
|
+
end
|
231
|
+
|
232
|
+
it 'is expected to raise an error with a invalid iat' do
|
233
|
+
token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc3NjUzNjEsIm5vbmNlIjoiYTFiMmMzZDRlNSIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU4NzY3ODk2MX0.1AeRLTaExbKnmsfNduUl3HArsau4RcNrnmYOJnkPWi0'
|
234
|
+
clock = CLOCK - LEEWAY - 1
|
235
|
+
instance = Auth0::Mixins::Validation::IdTokenValidator.new(CONTEXT.merge({ clock: clock }))
|
236
|
+
|
237
|
+
expect { instance.validate(token) }.to raise_exception("Issued At (iat) claim mismatch in the ID token; current time \"#{clock}\" is before issued at time \"1587765301\"")
|
238
|
+
end
|
239
|
+
|
240
|
+
it 'is expected not to raise an error with a missing but not required nonce' do
|
241
|
+
token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc1OTI1NjEsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU4NzY3ODk2MX0.-o5grnyODbBdRgzcrn7Sf9Hb6eOC0x_U2i3YjVgHN0U'
|
242
|
+
|
243
|
+
expect { @instance.validate(token) }.not_to raise_exception
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'is expected to raise an error with a missing but required nonce' do
|
247
|
+
token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc1OTI1NjEsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU4NzY3ODk2MX0.-o5grnyODbBdRgzcrn7Sf9Hb6eOC0x_U2i3YjVgHN0U'
|
248
|
+
instance = Auth0::Mixins::Validation::IdTokenValidator.new(CONTEXT.merge({ nonce: 'a1b2c3d4e5' }))
|
249
|
+
|
250
|
+
expect { instance.validate(token) }.to raise_exception('Nonce (nonce) claim must be a string present in the ID token')
|
251
|
+
end
|
252
|
+
|
253
|
+
it 'is expected to raise an error with an invalid nonce' do
|
254
|
+
token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc1OTI1NjEsIm5vbmNlIjoiMDAwOTk5IiwiYXpwIjoidG9rZW5zLXRlc3QtMTIzIiwiYXV0aF90aW1lIjoxNTg3Njc4OTYxfQ.XqQPdFN4m5kmTUQQi_mAJu0LQOeUTS9lF2J_xWc_j-0'
|
255
|
+
instance = Auth0::Mixins::Validation::IdTokenValidator.new(CONTEXT.merge({ nonce: 'a1b2c3d4e5' }))
|
256
|
+
|
257
|
+
expect { instance.validate(token) }.to raise_exception('Nonce (nonce) claim mismatch in the ID token; expected "a1b2c3d4e5", found "000999"')
|
258
|
+
end
|
259
|
+
|
260
|
+
it 'is expected to raise an error with a missing azp' do
|
261
|
+
token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc1OTI1NjEsIm5vbmNlIjoiYTFiMmMzZDRlNSIsImF1dGhfdGltZSI6MTU4NzY3ODk2MX0.LrgYkIbWrxMq6jvvkL1lxWL237ii1IBhtN2o_tDxFns'
|
262
|
+
|
263
|
+
expect { @instance.validate(token) }.to raise_exception('Authorized Party (azp) claim must be a string present in the ID token')
|
264
|
+
end
|
265
|
+
|
266
|
+
it 'is expected to raise an error with an invalid azp' do
|
267
|
+
token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc1OTI1NjEsIm5vbmNlIjoiYTFiMmMzZDRlNSIsImF6cCI6ImV4dGVybmFsLXRlc3QtOTk5IiwiYXV0aF90aW1lIjoxNTg3Njc4OTYxfQ.3DX-LY3B4UngDML-9nv11Sv89ECJpRpOLeWnkF1vAFY'
|
268
|
+
|
269
|
+
expect { @instance.validate(token) }.to raise_exception("Authorized Party (azp) claim mismatch in the ID token; expected \"tokens-test-123\", found \"external-test-999\"")
|
270
|
+
end
|
271
|
+
|
272
|
+
it 'is expected to raise an error with a missing auth_time' do
|
273
|
+
token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc1OTI1NjEsIm5vbmNlIjoiYTFiMmMzZDRlNSIsImF6cCI6InRva2Vucy10ZXN0LTEyMyJ9.JqUotnjHbGW0FcHz1s1YsRkce9Sbpsv2AEBDMpcUhp8'
|
274
|
+
instance = Auth0::Mixins::Validation::IdTokenValidator.new(CONTEXT.merge({ max_age: 120 }))
|
275
|
+
|
276
|
+
expect { instance.validate(token) }.to raise_exception('Authentication Time (auth_time) claim must be a number present in the ID token when Max Age (max_age) is specified')
|
277
|
+
end
|
278
|
+
|
279
|
+
it 'is expected to raise an error with a invalid auth_time' do
|
280
|
+
token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc1OTI1NjEsIm5vbmNlIjoiYTFiMmMzZDRlNSIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU4NzU5MjU2MX0.B7eWHJPHjhOh0ALjIQi0ro6zVsqGIeHd0gpRZsv6Hg8'
|
281
|
+
max_age = 120
|
282
|
+
auth_time = CLOCK + LEEWAY + max_age
|
283
|
+
clock = auth_time + 1
|
284
|
+
instance = Auth0::Mixins::Validation::IdTokenValidator.new(CONTEXT.merge({ max_age: max_age, clock: clock }))
|
285
|
+
|
286
|
+
expect { instance.validate(token) }.to raise_exception("Authentication Time (auth_time) claim in the ID token indicates that too much time has passed since the last end-user authentication. Current time \"#{clock}\" is after last auth at \"#{auth_time}\"")
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
describe Auth0::Algorithm::HS256 do
|
292
|
+
context 'class' do
|
293
|
+
it 'is expected to respond to :secret' do
|
294
|
+
expect(Auth0::Algorithm::HS256).to respond_to(:secret)
|
295
|
+
end
|
296
|
+
|
297
|
+
it 'is expected not to respond to :new' do
|
298
|
+
expect(Auth0::Algorithm::HS256).not_to respond_to(:new)
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
context 'instance' do
|
303
|
+
it 'is expected to respond to :secret' do
|
304
|
+
instance = Auth0::Algorithm::HS256.secret('secret')
|
305
|
+
|
306
|
+
expect(instance).to respond_to(:secret)
|
307
|
+
end
|
308
|
+
|
309
|
+
it 'is expected to return the secret' do
|
310
|
+
instance = Auth0::Algorithm::HS256.secret('secret')
|
311
|
+
|
312
|
+
expect(instance.secret).to eq('secret')
|
313
|
+
end
|
314
|
+
|
315
|
+
it 'is expected to return the algorithm name' do
|
316
|
+
instance = Auth0::Algorithm::HS256.secret('secret')
|
317
|
+
|
318
|
+
expect(instance.name).to eq('HS256')
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
context 'parameters' do
|
323
|
+
expected_error = 'Must supply a valid secret'
|
324
|
+
|
325
|
+
it 'is expected to raise an error with a nil secret' do
|
326
|
+
expect { Auth0::Algorithm::HS256.secret(nil) }.to raise_exception(expected_error)
|
327
|
+
end
|
328
|
+
|
329
|
+
it 'is expected to raise an error with an empty secret' do
|
330
|
+
expect { Auth0::Algorithm::HS256.secret('') }.to raise_exception(expected_error)
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
describe Auth0::Algorithm::RS256 do
|
336
|
+
before :each do
|
337
|
+
stub_jwks
|
338
|
+
end
|
339
|
+
|
340
|
+
after :each do
|
341
|
+
Auth0::Algorithm::RS256.remove_jwks
|
342
|
+
WebMock.reset!
|
343
|
+
end
|
344
|
+
|
345
|
+
context 'class' do
|
346
|
+
it 'is expected to respond to :jwks_url' do
|
347
|
+
expect(Auth0::Algorithm::RS256).to respond_to(:jwks_url)
|
348
|
+
end
|
349
|
+
|
350
|
+
it 'is expected not to respond to :new' do
|
351
|
+
expect(Auth0::Algorithm::RS256).not_to respond_to(:new)
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
context 'instance' do
|
356
|
+
it 'is expected to respond to :jwks' do
|
357
|
+
instance = Auth0::Algorithm::RS256.jwks_url('jwks url')
|
358
|
+
|
359
|
+
expect(instance).to respond_to(:jwks)
|
360
|
+
end
|
361
|
+
|
362
|
+
it 'is expected to respond to :fetched_jwks?' do
|
363
|
+
instance = Auth0::Algorithm::RS256.jwks_url('jwks url')
|
364
|
+
|
365
|
+
expect(instance).to respond_to(:fetched_jwks?)
|
366
|
+
end
|
367
|
+
|
368
|
+
it 'is expected to return a jwks' do
|
369
|
+
instance = Auth0::Algorithm::RS256.jwks_url(JWKS_URL)
|
370
|
+
|
371
|
+
expect(instance.jwks).to have_key('keys') and contain_exactly(a_hash_including(kid: 'test-key-1'))
|
372
|
+
end
|
373
|
+
|
374
|
+
it 'is expected to return if the jwks was fetched from the url' do
|
375
|
+
instance = Auth0::Algorithm::RS256.jwks_url(JWKS_URL)
|
376
|
+
instance.jwks
|
377
|
+
|
378
|
+
expect(instance.fetched_jwks?).to eq(true)
|
379
|
+
end
|
380
|
+
|
381
|
+
it 'is expected to return if the jwks was fetched from the cache' do
|
382
|
+
Auth0::Algorithm::RS256.jwks_url(JWKS_URL).jwks
|
383
|
+
instance = Auth0::Algorithm::RS256.jwks_url(JWKS_URL)
|
384
|
+
instance.jwks
|
385
|
+
|
386
|
+
expect(instance.fetched_jwks?).to eq(false)
|
387
|
+
end
|
388
|
+
|
389
|
+
it 'is expected to return the algorithm name' do
|
390
|
+
instance = Auth0::Algorithm::RS256.jwks_url('jwks url')
|
391
|
+
|
392
|
+
expect(instance.name).to eq('RS256')
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
context 'parameters' do
|
397
|
+
it 'is expected to raise an error with a nil jwks_url' do
|
398
|
+
expect { Auth0::Algorithm::RS256.jwks_url(nil) }.to raise_exception('Must supply a valid jwks_url')
|
399
|
+
end
|
400
|
+
|
401
|
+
it 'is expected to raise an error with an empty jwks_url' do
|
402
|
+
expect { Auth0::Algorithm::RS256.jwks_url('') }.to raise_exception('Must supply a valid jwks_url')
|
403
|
+
end
|
404
|
+
|
405
|
+
it 'is expected to raise an error with a non-integer lifetime' do
|
406
|
+
expect { Auth0::Algorithm::RS256.jwks_url('jwks url', lifetime: '1') }.to raise_exception('Must supply a valid lifetime')
|
407
|
+
end
|
408
|
+
|
409
|
+
it 'is expected to raise an error with a negative lifetime' do
|
410
|
+
expect { Auth0::Algorithm::RS256.jwks_url('jwks url', lifetime: -1) }.to raise_exception('Must supply a valid lifetime')
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
context 'cache' do
|
415
|
+
it 'is expected to fetch the jwks from the url when the cache is empty' do
|
416
|
+
instance = Auth0::Algorithm::RS256.jwks_url(JWKS_URL)
|
417
|
+
instance.jwks
|
418
|
+
|
419
|
+
expect(a_request(:get, JWKS_URL)).to have_been_made.once
|
420
|
+
end
|
421
|
+
|
422
|
+
it 'is expected to fetch the jwks from the url when the cache is expired' do
|
423
|
+
instance = Auth0::Algorithm::RS256.jwks_url(JWKS_URL, lifetime: 0)
|
424
|
+
instance.jwks
|
425
|
+
instance.jwks
|
426
|
+
|
427
|
+
expect(a_request(:get, JWKS_URL)).to have_been_made.twice
|
428
|
+
end
|
429
|
+
|
430
|
+
it 'is not expected to fetch the jwks from the url when there is a value cached' do
|
431
|
+
instance = Auth0::Algorithm::RS256.jwks_url(JWKS_URL)
|
432
|
+
instance.jwks
|
433
|
+
instance.jwks
|
434
|
+
|
435
|
+
expect(a_request(:get, JWKS_URL)).to have_been_made.once
|
436
|
+
end
|
437
|
+
|
438
|
+
it 'is expected to forcibly fetch the jwks from the url' do
|
439
|
+
instance = Auth0::Algorithm::RS256.jwks_url(JWKS_URL)
|
440
|
+
instance.jwks
|
441
|
+
instance.jwks(force: true)
|
442
|
+
|
443
|
+
expect(a_request(:get, JWKS_URL)).to have_been_made.twice
|
444
|
+
end
|
445
|
+
|
446
|
+
it 'is expected to forcibly fetch the jwks from the url and cache it' do
|
447
|
+
instance = Auth0::Algorithm::RS256.jwks_url(JWKS_URL)
|
448
|
+
instance.jwks(force: true)
|
449
|
+
instance.jwks
|
450
|
+
|
451
|
+
expect(a_request(:get, JWKS_URL)).to have_been_made.once
|
452
|
+
end
|
453
|
+
|
454
|
+
it 'is expected to return the last cached value if the jwks could not be fetched' do
|
455
|
+
Auth0::Algorithm::RS256.jwks_url(JWKS_URL).jwks
|
456
|
+
stub_request(:get, JWKS_URL).to_return(body: 'invalid')
|
457
|
+
instance = Auth0::Algorithm::RS256.jwks_url(JWKS_URL)
|
458
|
+
|
459
|
+
expect(instance.jwks).to have_key('keys') and contain_exactly(a_hash_including(kid: 'test-key-1'))
|
460
|
+
end
|
461
|
+
|
462
|
+
it 'is expected to raise an error if the jwks could not be fetched and the cache is empty' do
|
463
|
+
stub_request(:get, JWKS_URL).to_return(body: 'invalid')
|
464
|
+
instance = Auth0::Algorithm::RS256.jwks_url(JWKS_URL)
|
465
|
+
|
466
|
+
expect { instance.jwks }.to raise_exception('Could not fetch the JWK set')
|
467
|
+
end
|
468
|
+
end
|
469
|
+
end
|
470
|
+
# rubocop:enable Metrics/BlockLength
|
471
|
+
|
472
|
+
def stub_jwks(stub = JWKS_RESPONSE_1)
|
473
|
+
stub_request(:get, JWKS_URL).to_return(body: stub.to_json)
|
474
|
+
end
|