diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb
index a3014e056..204e689f1 100644
--- a/config/initializers/devise.rb
+++ b/config/initializers/devise.rb
@@ -22,10 +22,6 @@
# ==> Navigation configuration
config.sign_out_via = :get
- # ==> Omniauth configuration
- config.omniauth_path_prefix = '/admin/auth'
- config.omniauth :developer, fields: %i[email]
-
# ==> Warden configuration
# Reset the token after logging in so that other sessions are logged out
Warden::Manager.after_set_user except: :fetch do |user, warden, options|
diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb
index ed8872266..22bf0a143 100644
--- a/config/initializers/omniauth.rb
+++ b/config/initializers/omniauth.rb
@@ -1,3 +1,10 @@
-OmniAuth.configure do |config|
- config.logger = Rails.logger
+Rails.application.config.middleware.use OmniAuth::Builder do
+ configure do |config|
+ config.path_prefix = '/admin/auth'
+ config.logger = Rails.logger
+ end
+
+ IdentityProvider.each do |idp|
+ provider idp.name, idp.config
+ end
end
diff --git a/config/locales/admin.en-GB.yml b/config/locales/admin.en-GB.yml
index 501f50d66..abbaf34a5 100644
--- a/config/locales/admin.en-GB.yml
+++ b/config/locales/admin.en-GB.yml
@@ -74,6 +74,7 @@ en-GB:
invalidation_started: "Enqueued the invalidation %{summary}"
invalidation_updated: "Invalidation updated successfully"
invalid_login: "Invalid login details"
+ login_failed: "There was a problem logging in - please contact support"
logged_out: "You have been logged out"
moderator_required: "You must be logged in as a moderator or system administrator to view this page"
moderation_delay_sent: "An email has been sent to creators that moderation has been delayed"
diff --git a/config/routes.rb b/config/routes.rb
index 3a7af4515..e50ae9697 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -244,7 +244,6 @@
get '/', action: 'index', as: :stats
post '/', action: 'create', as: nil
end
-
end
devise_for :users, class_name: 'AdminUser', module: 'admin', skip: %i[sessions]
@@ -252,19 +251,25 @@
as :user do
controller 'admin/sessions' do
get '/admin/login', action: 'new'
+ post '/admin/login', action: 'create', as: nil
get '/admin/logout', action: 'destroy'
get '/admin/continue', action: 'continue'
get '/admin/status', action: 'status'
end
+
+ controller 'admin/omniauth_callbacks' do
+ get '/admin/auth/failure', action: 'failure'
+
+ scope '/admin/auth/:provider', via: %i[get post] do
+ match '/', action: 'passthru', as: :sso_provider
+ match '/callback', action: 'saml', as: :sso_provider_callback
+ end
+ end
end
end
# Devise needs a `new_user_session_url` helper for its failure app
direct(:new_user_session) { route_for(:admin_login) }
- # Friendly url helpers for Omniauth
- direct(:admin_auth_developer) { route_for(:user_developer_omniauth_authorize) }
- direct(:admin_auth_developer_callback) { route_for(:user_developer_omniauth_callback) }
-
get 'ping', to: 'ping#ping'
end
diff --git a/config/sso.yml b/config/sso.yml
new file mode 100644
index 000000000..e774e8c3e
--- /dev/null
+++ b/config/sso.yml
@@ -0,0 +1,13 @@
+development: []
+
+test:
+ - name: "example"
+ idp_sso_service_url: "http://localhost:3000/sso"
+ domains:
+ - "example.com"
+ sysadmins:
+ - "sysadmins"
+ moderators:
+ - "moderators"
+ reviewers:
+ - "reviewers"
diff --git a/features/admin/admin_access.feature b/features/admin/admin_access.feature
index 7bbafbb2a..1c44c96c0 100644
--- a/features/admin/admin_access.feature
+++ b/features/admin/admin_access.feature
@@ -15,12 +15,12 @@ Feature: Restricted access to the admin console
And I should be connected to the server via an ssl connection
And the markup should be valid
+ @javascript
Scenario: Login and logout to the admin console as a sysadmin
- Given a sysadmin user exists with email: "admin@example.com", first_name: "John", last_name: "Admin"
+ Given a sysadmin SSO user exists
When I go to the admin login page
- And I press "Login with developer strategy"
- And I fill in "Email" with "admin@example.com"
- And I press "Sign In"
+ And I fill in "Email" with "sysadmin@example.com"
+ And I press "Sign in"
Then I should be on the admin home page
And I should be connected to the server via an ssl connection
And the markup should be valid
@@ -29,22 +29,54 @@ Feature: Restricted access to the admin console
And I follow "Logout"
And I should be on the admin login page
- Scenario: Login and logout to the admin console as a moderator user
- Given a moderator user exists with email: "admin@example.com", first_name: "John", last_name: "Moderator"
+ @javascript
+ Scenario: Login and logout to the admin console as a moderator
+ Given a moderator SSO user exists
When I go to the admin login page
- And I press "Login with developer strategy"
- And I fill in "Email" with "admin@example.com"
- And I press "Sign In"
+ And I fill in "Email" with "moderator@example.com"
+ And I press "Sign in"
Then I should be on the admin home page
+ And I should be connected to the server via an ssl connection
+ And the markup should be valid
+ And I should not see "Users"
And I should see "John Moderator"
+ And I follow "Logout"
+ And I should be on the admin login page
+
+ @javascript
+ Scenario: Login and logout to the admin console as a reviewer
+ Given a reviewer SSO user exists
+ When I go to the admin login page
+ And I fill in "Email" with "reviewer@example.com"
+ And I press "Sign in"
+ Then I should be on the admin home page
+ And I should be connected to the server via an ssl connection
+ And the markup should be valid
And I should not see "Users"
+ And I should see "John Reviewer"
And I follow "Logout"
And I should be on the admin login page
+ Scenario: Invalid domain
+ When I go to the admin login page
+ And I fill in "Email" with "admin@example.org"
+ And I press "Sign in"
+ Then I should see "Invalid login details"
+ And should not see "Logout"
+
Scenario: Invalid login
- Given I go to the admin login page
- And I press "Login with developer strategy"
+ Given an invalid SSO login
+ When I go to the admin login page
And I fill in "Email" with "admin@example.com"
- And I press "Sign In"
+ And I press "Sign in"
+ Then I should see "There was a problem logging in - please contact support"
+ And should not see "Logout"
+
+ Scenario: Valid login but no role
+ Given a valid SSO login with no roles
+ When I go to the admin login page
+ And I fill in "Email" with "norole@example.com"
+ And I press "Sign in"
Then I should see "Invalid login details"
And should not see "Logout"
+
diff --git a/features/admin/admin_users.feature b/features/admin/admin_users.feature
index 4f9ce4b22..de406a8b8 100644
--- a/features/admin/admin_users.feature
+++ b/features/admin/admin_users.feature
@@ -5,7 +5,7 @@ Feature: Admin users index and crud
Background:
Given the time is "Sun, 17 Dec 2023 08:28:41 +0000"
- And I am logged in as a sysadmin with the email "muddy@fox.com", first_name "Sys", last_name "Admin"
+ And I am logged in as a sysadmin with the email "muddy@example.com", first_name "Sys", last_name "Admin"
And a moderator user exists with email: "naomi@example.com", first_name: "Naomi", last_name: "Campbell"
Scenario: Accessing the admin users index
@@ -20,7 +20,7 @@ Feature: Admin users index and crud
When I go to the admin users index page
Then I should see the following admin user table:
| Name | Email | Role | Last login |
- | Admin, Sys | muddy@fox.com | sysadmin | 08:28am on 17 December 2023 |
+ | Admin, Sys | muddy@example.com | sysadmin | 08:28am on 17 December 2023 |
| Campbell, Naomi | naomi@example.com | moderator | |
| Hunt, Helen | helen@example.com | moderator | 10:09am on 15 December 2023 |
| Jacobi, Derek | derek@example.com | moderator | 14:27pm on 11 December 2023 |
diff --git a/features/admin/anonymize_petitions.feature b/features/admin/anonymize_petitions.feature
index 46fc4ad31..5457e8f16 100644
--- a/features/admin/anonymize_petitions.feature
+++ b/features/admin/anonymize_petitions.feature
@@ -4,7 +4,7 @@ Feature: An admin anonymizes petitions
I want to anonymize all petitions 6 months after parliament closes in accordance with our privacy policy
Background:
- Given I am logged in as a sysadmin with the email "muddy@fox.com", first_name "Sys", last_name "Admin"
+ Given I am logged in as a sysadmin with the email "muddy@example.com", first_name "Sys", last_name "Admin"
@javascript
Scenario: Admin anonymizes petitions 6 months after parliament closes
diff --git a/features/step_definitions/authentication_steps.rb b/features/step_definitions/authentication_steps.rb
index f8c2fcd60..fc0cf7299 100644
--- a/features/step_definitions/authentication_steps.rb
+++ b/features/step_definitions/authentication_steps.rb
@@ -1,15 +1,15 @@
Given /^I am logged in as a sysadmin$/ do
- @user = FactoryBot.create(:sysadmin_user)
+ @user = FactoryBot.build(:sysadmin_sso_user)
step "the admin user is logged in"
end
Given /^I am logged in as a moderator$/ do
- @user = FactoryBot.create(:moderator_user)
+ @user = FactoryBot.build(:moderator_sso_user)
step "the admin user is logged in"
end
Given(/^I log out and login back in as a sysadmin$/) do
- @user = FactoryBot.create(:sysadmin_user)
+ @user = FactoryBot.build(:sysadmin_sso_user)
steps %q[
the admin user is logged out
@@ -18,7 +18,7 @@
end
Given(/^I log out and login back in as a moderator$/) do
- @user = FactoryBot.create(:moderator_user)
+ @user = FactoryBot.build(:moderator_sso_user)
steps %q[
the admin user is logged out
@@ -28,34 +28,49 @@
Given /^I am logged in as a moderator named "([^\"]*)"$/ do |name|
first_name, last_name = name.split
- @user = FactoryBot.create(:moderator_user, first_name: first_name, last_name: last_name)
- step "the admin user is logged in"
-end
-
-Given /^I am logged in as a moderator named "([^\"]*)" with the password "([^\"]*)"$/ do |name, password|
- first_name, last_name = name.split
- @user = FactoryBot.create(:moderator_user, first_name: first_name, last_name: last_name, :password => password, :password_confirmation => password)
+ @user = FactoryBot.build(:moderator_sso_user, first_name: first_name, last_name: last_name)
step "the admin user is logged in"
end
Given /^I am logged in as a sysadmin named "([^\"]*)"$/ do |name|
first_name, last_name = name.split
- @user = FactoryBot.create(:sysadmin_user, first_name: first_name, last_name: last_name)
+ @user = FactoryBot.build(:sysadmin_sso_user, first_name: first_name, last_name: last_name)
step "the admin user is logged in"
end
Given /^I am logged in as a sysadmin with the email "([^\"]*)", first_name "([^\"]*)", last_name "([^\"]*)"$/ do |email, first_name, last_name|
- @user = FactoryBot.create(:sysadmin_user, :email => email, :first_name => first_name, :last_name => last_name)
+ @user = FactoryBot.build(:sysadmin_sso_user, email: email, first_name: first_name, last_name: last_name)
step "the admin user is logged in"
end
Given /^the admin user is logged in$/ do
+ OmniAuth.config.mock_auth[:example] = @user
+
visit admin_login_url
- click_button("Login with developer strategy")
- fill_in("Email", :with => @user.email)
- click_button("Sign In")
+ fill_in("Email", with: @user.uid)
+ click_button("Sign in")
end
Given /^the admin user is logged out$/ do
visit admin_logout_url
end
+
+Given /^a sysadmin SSO user exists$/ do
+ OmniAuth.config.mock_auth[:example] = FactoryBot.build(:sysadmin_sso_user)
+end
+
+Given /^a moderator SSO user exists$/ do
+ OmniAuth.config.mock_auth[:example] = FactoryBot.build(:moderator_sso_user)
+end
+
+Given /^a reviewer SSO user exists$/ do
+ OmniAuth.config.mock_auth[:example] = FactoryBot.build(:reviewer_sso_user)
+end
+
+Given(/^a valid SSO login with no roles$/) do
+ OmniAuth.config.mock_auth[:example] = FactoryBot.build(:norole_sso_user)
+end
+
+Given /^an invalid SSO login$/ do
+ OmniAuth.config.mock_auth[:example] = :invalid_credentials
+end
diff --git a/features/support/hooks.rb b/features/support/hooks.rb
index 103d1aadf..1d3405adc 100644
--- a/features/support/hooks.rb
+++ b/features/support/hooks.rb
@@ -83,3 +83,16 @@
Before do
Rails.application.env_config['action_dispatch.show_detailed_exceptions'] = false
end
+
+Before do
+ OmniAuth.config.test_mode = true
+
+ OmniAuth.config.on_failure = Proc.new { |env|
+ OmniAuth::FailureEndpoint.new(env).redirect_to_failure
+ }
+end
+
+After do
+ OmniAuth.config.mock_auth[:example] = nil
+ OmniAuth.config.test_mode = false
+end
diff --git a/lib/package_builder/scripts/after_install.sh b/lib/package_builder/scripts/after_install.sh
index edf20d66e..2e3d18ebc 100644
--- a/lib/package_builder/scripts/after_install.sh
+++ b/lib/package_builder/scripts/after_install.sh
@@ -5,6 +5,7 @@ set -o pipefail
chown -R deploy:deploy /home/deploy/epetitions/releases/<%= release %>
su - deploy <<'EOF'
+ln -nfs /home/deploy/epetitions/shared/config/sso.yml /home/deploy/epetitions/releases/<%= release %>/config/sso.yml
ln -nfs /home/deploy/epetitions/shared/tmp /home/deploy/epetitions/releases/<%= release %>/tmp
ln -nfs /home/deploy/epetitions/shared/log /home/deploy/epetitions/releases/<%= release %>/log
ln -nfs /home/deploy/epetitions/shared/bundle /home/deploy/epetitions/releases/<%= release %>/vendor/bundle
diff --git a/spec/factories.rb b/spec/factories.rb
index 1fe7cc448..7ba8c74db 100644
--- a/spec/factories.rb
+++ b/spec/factories.rb
@@ -982,4 +982,82 @@
)
end
end
+
+ factory :sso_user, class: Hash do
+ transient do
+ email { "admin@example.com" }
+ first_name { "Sys" }
+ last_name { "Admin" }
+ groups { %w[sysadmins] }
+ end
+
+ provider { "example" }
+ uid { email }
+
+ info do
+ {
+ name: "#{first_name} #{last_name}",
+ email: email,
+ first_name: first_name,
+ last_name: last_name,
+ groups: groups
+ }
+ end
+
+ credentials do
+ {}
+ end
+
+ extra do
+ {
+ raw_info: {
+ name: "#{first_name} #{last_name}",
+ email: email,
+ first_name: first_name,
+ last_name: last_name,
+ groups: groups
+ }
+ }
+ end
+
+ skip_create
+
+ initialize_with { OmniAuth::AuthHash.new(attributes) }
+ end
+
+ factory :sysadmin_sso_user, parent: :sso_user do
+ transient do
+ email { "sysadmin@example.com" }
+ first_name { "John" }
+ last_name { "Admin" }
+ groups { %w[sysadmins] }
+ end
+ end
+
+ factory :moderator_sso_user, parent: :sso_user do
+ transient do
+ email { "moderator@example.com" }
+ first_name { "John" }
+ last_name { "Moderator" }
+ groups { %w[moderators] }
+ end
+ end
+
+ factory :reviewer_sso_user, parent: :sso_user do
+ transient do
+ email { "reviewer@example.com" }
+ first_name { "John" }
+ last_name { "Reviewer" }
+ groups { %w[reviewers] }
+ end
+ end
+
+ factory :norole_sso_user, parent: :sso_user do
+ transient do
+ email { "norole@example.com" }
+ first_name { "No" }
+ last_name { "Role" }
+ groups { %w[] }
+ end
+ end
end
diff --git a/spec/requests/admin_user_persistence_token_spec.rb b/spec/requests/admin_user_persistence_token_spec.rb
index 8c3679133..5dac1d99e 100644
--- a/spec/requests/admin_user_persistence_token_spec.rb
+++ b/spec/requests/admin_user_persistence_token_spec.rb
@@ -5,16 +5,17 @@
{
first_name: "System",
last_name: "Administrator",
- email: "admin@petition.parliament.uk"
+ email: "admin@example.com"
}
end
let(:login_params) do
- { email: "admin@petition.parliament.uk" }
+ { email: "admin@example.com" }
end
before do
- FactoryBot.create(:sysadmin_user, user_attributes)
+ sso_user = FactoryBot.create(:sysadmin_sso_user, **user_attributes)
+ OmniAuth.config.mock_auth[:example] = sso_user
end
def new_browser
@@ -28,16 +29,36 @@ def new_browser
context "when a new session is created" do
it "logs out existing sessions" do
s1 = new_browser
- s1.post "/admin/auth/developer/callback", params: login_params
+ s1.post "/admin/login", params: { user: login_params }
+
+ expect(s1.response.status).to eq(307)
+ expect(s1.response.location).to eq("https://moderate.petition.parliament.uk/admin/auth/example")
+
+ s1.follow_redirect!(params: s1.request.POST)
expect(s1.response.status).to eq(302)
- expect(s1.response.location).to eq("https://moderate.petition.parliament.uk/admin")
+ expect(s1.response.location).to eq("https://moderate.petition.parliament.uk/admin/auth/example/callback")
+
+ s1.follow_redirect!
+
+ expect(s1.response.status).to eq(200)
+ expect(s1.response).to have_header("Refresh", "0; url=https://moderate.petition.parliament.uk/admin")
s2 = new_browser
- s2.post "/admin/auth/developer/callback", params: login_params
+ s2.post "/admin/login", params: { user: login_params }
+
+ expect(s2.response.status).to eq(307)
+ expect(s2.response.location).to eq("https://moderate.petition.parliament.uk/admin/auth/example")
+
+ s2.follow_redirect!(params: s2.request.POST)
expect(s2.response.status).to eq(302)
- expect(s2.response.location).to eq("https://moderate.petition.parliament.uk/admin")
+ expect(s2.response.location).to eq("https://moderate.petition.parliament.uk/admin/auth/example/callback")
+
+ s2.follow_redirect!
+
+ expect(s2.response.status).to eq(200)
+ expect(s2.response).to have_header("Refresh", "0; url=https://moderate.petition.parliament.uk/admin")
s1.get("/admin")
@@ -49,10 +70,20 @@ def new_browser
context "when a session is destroyed" do
it "resets the persistence token" do
s1 = new_browser
- s1.post "/admin/auth/developer/callback", params: login_params
+ s1.post "/admin/login", params: { user: login_params }
+
+ expect(s1.response.status).to eq(307)
+ expect(s1.response.location).to eq("https://moderate.petition.parliament.uk/admin/auth/example")
+
+ s1.follow_redirect!(params: s1.request.POST)
expect(s1.response.status).to eq(302)
- expect(s1.response.location).to eq("https://moderate.petition.parliament.uk/admin")
+ expect(s1.response.location).to eq("https://moderate.petition.parliament.uk/admin/auth/example/callback")
+
+ s1.follow_redirect!
+
+ expect(s1.response.status).to eq(200)
+ expect(s1.response).to have_header("Refresh", "0; url=https://moderate.petition.parliament.uk/admin")
s2 = new_browser
s2.cookies["_epets_session"] = s1.cookies["_epets_session"]
@@ -79,8 +110,20 @@ def new_browser
Site.instance.update(login_timeout: 600)
travel_to 5.minutes.ago do
- post "/admin/auth/developer/callback", params: login_params
- expect(response).to redirect_to("/admin")
+ post "/admin/login", params: { user: login_params }
+
+ expect(response.status).to eq(307)
+ expect(response.location).to eq("https://moderate.petition.parliament.uk/admin/auth/example")
+
+ follow_redirect!(params: request.POST)
+
+ expect(response.status).to eq(302)
+ expect(response.location).to eq("https://moderate.petition.parliament.uk/admin/auth/example/callback")
+
+ follow_redirect!
+
+ expect(response.status).to eq(200)
+ expect(response).to have_header("Refresh", "0; url=https://moderate.petition.parliament.uk/admin")
end
get "/admin"
diff --git a/spec/requests/csrf_token_spec.rb b/spec/requests/csrf_token_spec.rb
index b2b3e308d..4e94edbab 100644
--- a/spec/requests/csrf_token_spec.rb
+++ b/spec/requests/csrf_token_spec.rb
@@ -5,12 +5,17 @@
{
first_name: "System",
last_name: "Administrator",
- email: "admin@petition.parliament.uk"
+ email: "admin@example.com"
}
end
let(:login_params) do
- { email: "admin@petition.parliament.uk" }
+ { email: "admin@example.com" }
+ end
+
+ before do
+ sso_user = FactoryBot.create(:sysadmin_sso_user, **user_attributes)
+ OmniAuth.config.mock_auth[:example] = sso_user
end
let(:encrypted_csrf_token) do
@@ -31,20 +36,36 @@
get "/admin/login"
end
+ def do_login
+ post "/admin/login", params: { user: login_params, authenticity_token: authenticity_token }
+
+ expect(response.status).to eq(307)
+ expect(response.location).to eq("https://moderate.petition.parliament.uk/admin/auth/example")
+
+ follow_redirect!(params: request.POST)
+
+ expect(response.status).to eq(302)
+ expect(response.location).to eq("https://moderate.petition.parliament.uk/admin/auth/example/callback")
+
+ follow_redirect!
+
+ expect(response.status).to eq(200)
+ expect(response).to have_header("Refresh", "0; url=https://moderate.petition.parliament.uk/admin")
+ end
+
context "when a new session is created" do
it "resets the csrf token" do
expect {
- post "/admin/auth/developer/callback", params: login_params
+ do_login
}.to change {
session[:_csrf_token]
- }.from(be_present).to(be_nil)
+ }
end
end
context "when a session is destroyed" do
before do
- post "/admin/auth/developer/callback", params: login_params
- follow_redirect!
+ do_login
end
it "resets the csrf token" do
@@ -61,8 +82,7 @@
Site.instance.update(login_timeout: 600)
travel_to 5.minutes.ago do
- post "/admin/auth/developer/callback", params: login_params
- expect(response).to redirect_to("/admin")
+ do_login
end
get "/admin"
diff --git a/spec/requests/login_timeout_spec.rb b/spec/requests/login_timeout_spec.rb
index 8973298f9..89ff55fec 100644
--- a/spec/requests/login_timeout_spec.rb
+++ b/spec/requests/login_timeout_spec.rb
@@ -5,15 +5,18 @@
{
first_name: "System",
last_name: "Administrator",
- email: "admin@petition.parliament.uk"
+ email: "admin@example.com"
}
end
let(:login_params) do
- { email: "admin@petition.parliament.uk" }
+ { email: "admin@example.com" }
end
- let!(:user) { FactoryBot.create(:sysadmin_user, user_attributes) }
+ before do
+ sso_user = FactoryBot.create(:sysadmin_sso_user, **user_attributes)
+ OmniAuth.config.mock_auth[:example] = sso_user
+ end
before do
host! "moderate.petition.parliament.uk"
@@ -24,8 +27,20 @@
Site.instance.update(login_timeout: 600)
travel_to 2.minutes.ago do
- post "/admin/auth/developer/callback", params: login_params
- expect(response).to redirect_to("/admin")
+ post "/admin/login", params: { user: login_params }
+
+ expect(response.status).to eq(307)
+ expect(response.location).to eq("https://moderate.petition.parliament.uk/admin/auth/example")
+
+ follow_redirect!(params: request.POST)
+
+ expect(response.status).to eq(302)
+ expect(response.location).to eq("https://moderate.petition.parliament.uk/admin/auth/example/callback")
+
+ follow_redirect!
+
+ expect(response.status).to eq(200)
+ expect(response).to have_header("Refresh", "0; url=https://moderate.petition.parliament.uk/admin")
end
get "/admin"
diff --git a/spec/support/omniauth.rb b/spec/support/omniauth.rb
new file mode 100644
index 000000000..841ce48f0
--- /dev/null
+++ b/spec/support/omniauth.rb
@@ -0,0 +1,16 @@
+RSpec.configure do |config|
+ config.around(:each, type: :request) do |example|
+ OmniAuth.config.test_mode = true
+ existing_failure_proc = OmniAuth.config.on_failure
+
+ OmniAuth.config.on_failure = Proc.new { |env|
+ OmniAuth::FailureEndpoint.new(env).redirect_to_failure
+ }
+
+ example.run
+ ensure
+ OmniAuth.config.mock_auth[:example] = nil
+ OmniAuth.config.on_failure = existing_failure_proc
+ OmniAuth.config.test_mode = false
+ end
+end