Fix Google SSO boot order and route plumbing surfaced by specs

Three fixes uncovered while running the new omniauth_callbacks specs in
a Ruby 4.0.1 container:

- config/initializers/devise.rb: read GOOGLE_CLIENT_ID / SECRET /
  ALLOWED_DOMAINS directly from ENV instead of via Wabosign::*. The
  module isn't autoloadable yet at initializer-load time (Rails.root
  isn't set), but ENV is. The User model and controllers still go
  through Wabosign helpers, which load fine once Rails is up.

- app/models/user.rb: stop passing `omniauth_providers:` when
  :omniauthable isn't in the modules list. Devise raises
  NoMethodError omniauth_providers= otherwise. Now both the module
  inclusion and the keyword are gated on Wabosign.google_sso_enabled?

- spec/requests/users/omniauth_callbacks_spec.rb: post to
  user_google_oauth2_omniauth_callback_path instead of the hardcoded
  /users/auth/... URL. With devise_for :users, path: '/' the actual
  callback route is /auth/google_oauth2/callback. Also create a
  placeholder admin user so ApplicationController#maybe_redirect_to_setup
  doesn't intercept the request before the callback action runs.

Schema dump and .gitignore (adds /vendor) bundled in.

All 5 specs now pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
pull/687/head
Wabo 1 month ago
parent c77744fea1
commit 45ed368a26

1
.gitignore vendored

@ -38,3 +38,4 @@ yarn-debug.log*
/ee
dump.rdb
*.onnx
/vendor

@ -70,8 +70,12 @@ class User < ApplicationRecord
has_many :email_messages, dependent: :destroy, foreign_key: :author_id, inverse_of: :author
devise_modules = %i[two_factor_authenticatable recoverable rememberable validatable trackable lockable]
devise_modules << :omniauthable if Wabosign.google_sso_enabled?
devise(*devise_modules, omniauth_providers: [:google_oauth2])
devise_opts = {}
if Wabosign.google_sso_enabled?
devise_modules << :omniauthable
devise_opts[:omniauth_providers] = [:google_oauth2]
end
devise(*devise_modules, **devise_opts)
attribute :role, :string, default: ADMIN_ROLE
attribute :uuid, :string, default: -> { SecureRandom.uuid }

@ -334,15 +334,22 @@ Devise.setup do |config|
# changed. Defaults to true, so a user is signed in automatically after changing a password.
# config.sign_in_after_change_password = true
if Wabosign.google_sso_enabled?
# NB: Wabosign-the-module relies on Rails.root, which isn't available yet
# when this initializer runs. Read ENV directly here so the omniauth strategy
# can be registered at boot. Controllers/models access the same values via
# Wabosign::GOOGLE_* once Rails is fully initialized.
google_client_id = ENV.fetch('GOOGLE_CLIENT_ID', nil)
google_client_secret = ENV.fetch('GOOGLE_CLIENT_SECRET', nil)
if google_client_id.present? && google_client_secret.present?
config.omniauth :google_oauth2,
Wabosign::GOOGLE_CLIENT_ID,
Wabosign::GOOGLE_CLIENT_SECRET,
google_client_id,
google_client_secret,
{
scope: 'email,profile',
prompt: 'select_account',
access_type: 'online',
hd: Wabosign::GOOGLE_ALLOWED_DOMAINS.presence
hd: ENV.fetch('GOOGLE_ALLOWED_DOMAINS', '')
.split(',').map(&:strip).reject(&:empty?).presence
}
end

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[8.1].define(version: 2026_05_06_121640) do
ActiveRecord::Schema[8.1].define(version: 2026_05_15_200000) do
# These are extensions that must be enabled in order to support this database
enable_extension "btree_gin"
enable_extension "pg_catalog.plpgsql"
@ -513,17 +513,20 @@ ActiveRecord::Schema[8.1].define(version: 2026_05_06_121640) do
t.datetime "locked_at"
t.boolean "otp_required_for_login", default: false, null: false
t.string "otp_secret"
t.string "provider"
t.datetime "remember_created_at"
t.datetime "reset_password_sent_at"
t.string "reset_password_token"
t.string "role", null: false
t.integer "sign_in_count", default: 0, null: false
t.string "uid"
t.string "unconfirmed_email"
t.string "unlock_token"
t.datetime "updated_at", null: false
t.string "uuid", null: false
t.index ["account_id"], name: "index_users_on_account_id"
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["provider", "uid"], name: "index_users_on_provider_and_uid", unique: true, where: "(provider IS NOT NULL)"
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
t.index ["unlock_token"], name: "index_users_on_unlock_token", unique: true
t.index ["uuid"], name: "index_users_on_uuid", unique: true

@ -4,6 +4,9 @@ require 'rails_helper'
RSpec.describe 'Google OAuth2 callback', type: :request do
let!(:account) { create(:account) }
# ApplicationController redirects to /setup when no users exist; create a
# placeholder admin so that branch doesn't fire during these specs.
let!(:placeholder_admin) { create(:user, account: account, email: 'admin@wabo.cc') }
before do
OmniAuth.config.test_mode = true
@ -34,7 +37,7 @@ RSpec.describe 'Google OAuth2 callback', type: :request do
stub_google_auth(email: 'new.user@wabo.cc')
expect do
post '/users/auth/google_oauth2/callback'
post user_google_oauth2_omniauth_callback_path
end.to change(User, :count).by(1)
user = User.find_by(email: 'new.user@wabo.cc')
@ -52,7 +55,7 @@ RSpec.describe 'Google OAuth2 callback', type: :request do
stub_google_auth(email: 'existing@wabo.cc', uid: 'google-uid-99')
expect do
post '/users/auth/google_oauth2/callback'
post user_google_oauth2_omniauth_callback_path
end.not_to change(User, :count)
user.reload
@ -67,7 +70,7 @@ RSpec.describe 'Google OAuth2 callback', type: :request do
stub_google_auth(email: 'outsider@evil.com', hd: 'evil.com')
expect do
post '/users/auth/google_oauth2/callback'
post user_google_oauth2_omniauth_callback_path
end.not_to change(User, :count)
expect(response).to redirect_to(new_user_session_path)
@ -85,7 +88,7 @@ RSpec.describe 'Google OAuth2 callback', type: :request do
it 'rejects sign-in when the email is linked to a different Google uid' do
stub_google_auth(email: 'taken@wabo.cc', uid: 'different-uid')
post '/users/auth/google_oauth2/callback'
post user_google_oauth2_omniauth_callback_path
user.reload
expect(user.uid).to eq('original-uid')
@ -103,7 +106,7 @@ RSpec.describe 'Google OAuth2 callback', type: :request do
it 'signs the user in via Google without prompting for OTP' do
stub_google_auth(email: '2fa@wabo.cc', uid: '2fa-uid')
post '/users/auth/google_oauth2/callback'
post user_google_oauth2_omniauth_callback_path
expect(response).to redirect_to(root_path)
get root_path

Loading…
Cancel
Save