Until now, Google SSO required setting GOOGLE_CLIENT_ID /
GOOGLE_CLIENT_SECRET / GOOGLE_ALLOWED_DOMAINS in the environment and
restarting the container. This commit adds a UI-driven configuration
path that doesn't need a restart, while keeping ENV as the priority
source for production deployments.
Storage: new EncryptedConfig key `google_sso_configs` (added to
CONFIG_KEYS) with shape:
{ enabled: bool, client_id, client_secret, allowed_domains: [..] }
The secret rides on Rails' `encrypts :value` like every other
EncryptedConfig record.
Strategy registration: the Devise initializer now always registers
:google_oauth2 with a setup proc, so the omniauth routes exist
unconditionally. The setup proc calls Wabosign.google_sso_credentials
per request — that helper checks ENV first (priority) and falls back
to the DB. Empty creds yield :source => :none and the Google button
is hidden by the sign-in partial.
User model: :omniauthable + omniauth_providers: [:google_oauth2] are
now unconditional (matches the always-registered route). The
boot-time fragile gating that broke `bundle exec puma` when env vars
weren't set is gone.
Routes: omniauth_callbacks no longer depends on ENV. /settings/sso
gains a :create action. SsoSettingsController#create persists the
form payload via the existing EncryptedConfig pattern (and never
overwrites a saved secret with a blank).
View: /settings/sso is now a real form (client_id, client_secret,
allowed_domains, enabled toggle) instead of an env-only status panel.
A banner explains ENV precedence when GOOGLE_CLIENT_ID is set. The
redirect URI to register in Google Cloud Console is shown in the
"not configured" state.
User#default_sso_account now prefers the account that owns the
UI-saved config so JIT-provisioned users land in the right tenant
when an admin sets up SSO from the UI in a multi-account deployment.
Specs: the omniauth_callbacks request specs were stubbing the removed
Wabosign::GOOGLE_* constants. Switched them to
`allow(Wabosign).to receive(:google_sso_credentials)`. All 5 pass.
Smoke-tested the rebuilt image in three states:
- No ENV, no DB: container boots, /sign_in 200, no button.
- DB config saved: button appears on the very next /sign_in render.
- ENV set + DB set: ENV wins (allowed_domains and creds come from ENV).
Docs: GOOGLE_SSO.md gains a section describing the UI path and how
the two sources interact.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>