CHANGELOG entry for the merge of upstream tag 3.0.0 plus the
rebrand-sync / rebrand-check tooling added in 6b652f8a. .version
bumped to 1.2.0 so the in-app footer + image label reflect the new
release.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
First exercise of the bin/rebrand-sync workflow. Upstream 3.0.0
(merge-base 528a1216, 15 commits) brought:
Auto-merged cleanly (no conflict):
- New controllers/routes: submissions_resend_email (route added by
hand to config/routes.rb since fork's routes diverged elsewhere),
template_documents, templates_clone_and_replace, templates_folders,
templates_restore, templates_versions, submissions_unarchive
- PDF optimizations and signing-form completion-button refactor
(e378025a, 04129ded, 7fe56941)
- Percent formatting (99ca0136), area-box clamping (41604008),
validation message rephrasings (abd498dd)
Conflicts resolved (rerere now caches these for next sync):
- "take ours" for files where the fork stripped freemium gates
(Plans/Console/Upgrade, ENTERPRISE_PATHS, multitenant guards on
esign default sig, reminder durations, decline/delegate toggles,
BCC and send-on-completion, Pro upsell placeholders for SMS/SSO/
bulk-send/payment/conditions/formula/phone-field)
- "take ours" for per-account branding helpers
(Wabosign.branded_product_name vs Wabosign.product_name) across
mailers, MFA, MCP, audit-trail PDFs, page titles
- "take ours" for fork brand URLs (sign.wabo.cc, Wabosign::PRODUCT_URL,
Wabosign::GITHUB_URL) over upstream's hardcoded wabosign.com
- "take ours" for the webhook User-Agent ("WaboSign Webhook" not
"WaboSign.com Webhook") and X-Wabosign-Signature header
- "take theirs" for submitters_send_email's defensive authorize!(:update)
(security improvement from upstream commit e52830c9)
- Hybrid resolution in lib/send_webhook_request.rb — keep fork's
USER_AGENT, take upstream's "don't override custom webhook header"
blank-check (a7891f89)
- 22 "deleted by us" files (Pro upsell controllers/views, removed
docs/api/*, deleted newsletter feature) confirmed deleted
Known gaps to fix as follow-ups:
- config/locales/i18n.yml — taken ours wholesale; missing upstream's
three new resend-email keys (re_send_emails,
are_you_sure_you_want_to_re_send_email_to_n_recipients,
emails_have_been_sent_to_n_recipients) across all 14 languages.
English fallback works for those strings until translated.
- Gemfile.lock — taken ours; needs `bundle install` to regenerate
with upstream's gem updates from 37d4a8e8.
- yarn.lock — same; needs `yarn install`.
- bin/rebrand-sync — the `\bdocuseal_` rule misses `_docuseal_` inside
identifiers (e.g. `unlock_with_docuseal_pro` i18n keys). Widening
to `docuseal_` (no leading word boundary) would catch those. Tracked
as a script refinement before the next sync.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Output of bin/rebrand-sync against the upstream 3.0.0 tree. Renames
lib/docuseal.rb → lib/wabosign.rb, rewrites the Docuseal/DocuSeal
identifiers to Wabosign/WaboSign across controllers, jobs, mailers,
views, JS/Vue, locales, specs, and infrastructure (Dockerfile,
docker-compose, CI workflows, env-file path, AATL cert name).
This commit is the sync branch's contribution to the merge — it does
NOT carry the fork's features (SSO, SMS, per-account branding, etc.)
or the bin/rebrand-* scripts themselves; those arrive via the
subsequent merge into master.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds a deterministic rebrand sweep so each upstream sync is a scripted
transformation rather than a manual sweep:
- bin/rebrand-sync — idempotent Ruby script that rewrites DocuSeal →
WaboSign tokens across the tree (Ruby module, AATL cert name, DB
names, Docker user, registry/repo URLs, DOM-ID/localStorage prefix,
daisyUI theme name, hardcoded UI strings). Sentinel-protects AGPL
§7(b) attribution phrases, SDK custom-element identifiers, the
@docuseal/* npm packages, and the docusealco/{fields-detection,
pdfium-binaries,turbo} binary URLs. Deny-lists NOTICE, LICENSE_*,
README, the AGPL attribution partials, calculator.js's DocuSeal LLC
copyright, lib/wabosign.rb's UPSTREAM constants, and the
docuseal_aatl migration that searches by the legacy name.
- bin/rebrand-check — fails CI when an unintended DocuSeal reference
survives in the tree. Wired in as the new `Rebrand check` job in
.github/workflows/ci.yml.
- REBRANDING.md gains a "Sync workflow" section documenting the
per-sync steps, rerere setup, and how to keep the two scripts'
allow/preserve lists in sync.
- .gitattributes marks Gemfile.lock and yarn.lock as -merge — they get
regenerated post-merge rather than diffed.
- Latent rebrand leftovers fixed: public/service-worker.js no longer
logs "DocuSeal App installed/activated"; .dockerignore and
.gitignore now ignore /wabosign (the actual runtime data dir mount)
rather than the stale /docuseal path.
Strategy detail lives at .claude/plans/come-up-with-a-foamy-flask.md.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Account admins can now replace "WaboSign" in the UI, emails, audit-trail
PDFs, and authenticator-app issuer with their own product name. The
brand override is stored as an AccountConfig row (brand_name key),
managed from /settings/personalization above the logo upload.
Resolution flows through Wabosign.branded_product_name(account = nil):
1. account&.brand_name if a record is passed
2. else the deployment's oldest non-archived account's brand_name
(so anonymous surfaces like the landing page, PWA manifest, and
og:title get the operator's brand on single-tenant installs)
3. else Wabosign::PRODUCT_NAME ("WaboSign")
AGPL §7(b) DocuSeal attribution stays untouched:
- _powered_by.html.erb second line keeps Wabosign::UPSTREAM_NAME
- _email_attribution.html.erb second paragraph keeps it
- completed.vue keeps its hardcoded DocuSeal link
The Wabosign::UPSTREAM_NAME and UPSTREAM_URL constants stay constants —
they are never overridable.
Swapped 41 direct Wabosign.product_name callers to pass the most-local
account in scope (current_account, @template.account,
@submitter.submission.account, submission.account, or nil for chrome
without account context). Mailers' default `from:` is now a lambda that
reads @current_account per message. SIGN_REASON constant in
generate_result_attachments became sign_reason_template(account) so
PDF signature reasons reflect the brand.
The two i18n keys actually rendered with literal "WaboSign"
(welcome_to_wabosign in templates_dashboard, connect_to_wabosign_mcp
in mcp_settings) are parameterized to %{product_name} across the 7
locales that defined them. The other ~9 WaboSign-branded i18n keys
are unreferenced dead code from the Pro paywall and stay as-is.
Specs:
spec/models/account_spec.rb (new) — Account#brand_name
spec/lib/wabosign_spec.rb (new) — branded_product_name precedence
spec/requests/personalization_settings_spec.rb (new) — end-to-end
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- actions/checkout@v3 -> @v4 in docker.yml
- actions/setup-node@v1 -> @v4 in ci.yml (both jobs that use it)
- Replace deprecated `::set-output` with $GITHUB_OUTPUT in the
ESLint yarn-cache-dir step
The remaining Node 20 deprecation warnings on actions/cache@v4,
actions/checkout@v4, and the docker/* actions will resolve once
upstream ships Node 24 builds; not blocking, the runtime won't
flip until 2026-06.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
erblint's autocorrect on the prior commit stripped the opening
<% tag from the variable-assignment block in
app/views/shared/_account_logo.html.erb, leaving plain Ruby code as
literal HTML and a NameError on every render. The "Failure/Error:
<% if acc&.logo&.attached? %>" cascade in CI was 40+ specs hitting
this partial via the navbar / start_form / submit_form / share-link
QR / submission show paths. Restoring the proper <% ... %> tag.
Also drop the now-redundant `# rubocop:disable Rails/Exit` comments
in spec/rails_helper.rb (CI's rubocop-rails doesn't flag those
lines) and add the same path to .rubocop.yml's Rails/Exit Exclude
so the project's local rubocop agrees.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Previously CI had never run on the wabolabs/wabosign fork (Actions
gated until owner consent). Now that the gate is lifted, run rubocop /
erblint / brakeman / rspec against current master uncovered backlog:
- rubocop: 97 auto-corrected across the WaboSign-fork files (account
logo, SMS, SSO, ability specs, role auth specs, omniauth callbacks).
Remaining 8 fixed by hand:
* lib/wabosign.rb chained map collapsed to filter_map; `hd` param
renamed to `hosted_domain` (Naming/MethodParameterName)
* app/models/user.rb default_sso_account split for line length +
SafeNavigation
* spec/rails_helper.rb abort calls marked `# rubocop:disable
Rails/Exit` (upstream pattern, intentional)
* spec/requests/users/omniauth_callbacks_spec.rb let! used for
side-effect-only setup -> moved into before blocks
- erblint: 21 auto-corrected (mostly Style/StringLiterals from a
sed substitution that picked double quotes) + a missing
autocomplete attribute added to the SMS test-message input.
- brakeman: clean. Removed one obsolete ignore entry (was for the
deleted enquiries controller) and added one new ignore for the
MCP-settings token preview (HighlightCode returns escaped HTML).
- rspec: dashboard "shows the list of templates" was flaky because
other_template's Faker::Book.title could randomly collide with one
of the 5 in-account templates. Pin the name to a unique suffix.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sweep of stale upstream cruft and missing release-process plumbing:
- SECURITY.md routes vuln reports to wabosign@wabo.cc
- package.json renamed wabosign + license/repository fields
- Drop the docuseal.com fallback href in signature_step.vue
- Delete docs/api/ (10 upstream language stubs) and rewrite stale
docuseal.com URLs in docs/openapi.json + docs/embedding/* +
docs/webhooks/* to sign.wabo.cc
- Remove console_redirect + enquiries controllers and routes
(/upgrade, /manage, /console_redirect were DocuSeal-SaaS-only).
Strip the navbar Console icon, the embed_scripts upgrade-to-Pro
fallback, the sessions_controller CONSOLE_URL redirect, and the
CONSOLE_URL/CDN_URL/CLOUD_URL/ENQUIRIES_URL constants. The four
"Learn more" links in templates/_embedding.html.erb now point at
sign.wabo.cc/docs/embedding; the two CDN script-src refs use the
local embed_script_url helper.
- Dockerfile gains OCI image labels via ARG VERSION/REVISION
- docker.yml passes labels + build-args from metadata-action@v5 so
the published image has the right manifest-level metadata
- Add CHANGELOG.md (Keep-a-Changelog) and a Releases section in README
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The first-run setup flow ended by redirecting the new admin to a
DocuSeal-branded newsletter signup that POSTed to the upstream
project's mailing-list endpoint. That has no place in WaboSign —
strip the controller, view, route, helper constant, i18n keys (7
locales), and system spec, and redirect setup#create straight to
root_path.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Splits the previously single-role Ability class along role boundaries.
Admin keeps full account management. Editor gets CRUD on templates,
folders, submissions, submitters, and template sharings, but cannot
touch users, account settings, encrypted configs, webhooks, or MCP.
Viewer gets read-only access to the same content surface. Every role
keeps self-service on their own User / UserConfig / AccessToken.
UsersController#index gains a one-line admin guard so non-admins
cannot reach the user list via the self-manage rule's class-level
CanCan check.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replaces the disabled "logo upload not bundled in OSS" placeholder
with a real upload flow. Logos attach to the Account via ActiveStorage
(`has_one_attached :logo`) and replace the default WaboSign mark at
every render site that previously rendered `shared/_logo`.
Accepted formats: PNG, JPEG, and SVG. SVGs go through
`AccountLogo.sanitize_upload` before storage:
- `<script>` and `<foreignObject>` elements removed
- every `on*` attribute stripped (onclick, onload, onerror, …)
- `href` / `xlink:href` dropped unless the value starts with `#`
(in-doc fragment) or `data:` (inert in <img> context)
Raster uploads pass through unchanged. 2 MB upload cap on all formats.
Dispatch is a new `shared/_account_logo.html.erb` partial — takes an
optional `account:` local and emits either `<img>` (logo attached) or
falls back to the existing inline `shared/_logo.html.erb` SVG. All
swapped render sites pass the right account expression:
- shared/_title.html.erb → current_account
- start_form/_brand_logo.html.erb → @template.account
- submit_form/_brand_logo.html.erb → @submitter.submission.account
- templates_uploads/show.html.erb → current_account
- submissions/_logo.html.erb → @submission || @submitter accounts
- templates_share_link_qr/_logo.html.erb → @template.account
The landing page stays on the default mark (no account context).
Static favicon/PWA manifest/preview.png stay on the default brand.
Audit-trail PDF (`lib/submissions/generate_audit_trail.rb#add_logo`)
now calls `PdfIcons.account_logo_io(submission.account)`. SVG logos
are rasterized to PNG via ActiveStorage variants (libvips + librsvg,
both present in the production image via the `vips` Alpine pkg). On
any failure path the helper logs and falls back to `PdfIcons.logo_io`
so audit-trail generation never crashes on a bad logo.
Routes: `resource :account_logo, only: %i[create destroy]` nested
under `/settings`. AccountLogoController authorizes via
`authorize!(:manage, current_account)` and routes the form back to
`/settings/personalization` on success or failure.
Specs (11/11 pass in the Ruby 4.0.1 + Postgres-14 container):
- spec/lib/account_logo_spec.rb — 6 sanitizer unit cases
- spec/requests/account_logo_controller_spec.rb — 5 request cases
Smoke-tested end-to-end in the built image:
- PNG round-trips through attach/download.
- Malicious SVG (`<script>` + onload + alert) saves with all three
payloads scrubbed from the stored bytes.
- SVG → PNG rasterization for the PDF path produces a 18 KB PNG
with valid PNG magic — confirms libvips/librsvg is actually
wired in the production Alpine image.
- After purge, `PdfIcons.account_logo_io` is byte-identical to
`PdfIcons.logo_io` (clean fallback).
- /settings/personalization renders the new form with all expected
fields.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Operator-facing reference for the BulkVS integration:
configuration, send pipeline, code map, BulkVS API details,
how to add another provider, and explicit out-of-scope items.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replaces the SMS placeholder with an actual provider integration. v1
ships BulkVS only; the architecture leaves room for additional
providers behind the same Sms.send_message interface.
Storage:
- EncryptedConfig key `sms_configs` (added to CONFIG_KEYS):
{ provider, enabled, basic_auth_token, from_number,
delivery_webhook_url }
- AccountConfig key `submitter_invitation_sms` for the per-account
SMS body template override.
Service layer:
- lib/sms.rb — Sms.enabled_for?(account), Sms.send_message
(account:, to:, text:), Sms.normalize_phone
- lib/sms/providers/bulkvs.rb — POST to
https://portal.bulkvs.com/api/v1.0/messageSend with the
pre-encoded Basic Auth header from the BulkVS portal. Surfaces
non-2xx responses as Sms::ProviderError with the upstream message.
Background sending:
- app/jobs/send_submitter_invitation_sms_job.rb — mirrors
SendSubmitterInvitationEmailJob; substitutes account-template
variables via the existing ReplaceEmailVariables module so
{account.name} / {submitter.link} / etc. work in the SMS body.
- submitters_controller#maybe_resend_email_sms already enqueues
this job when params[:send_sms] == '1', so the existing
"Send SMS" toggle in the submitter edit form now does what it
says on the tin.
Controllers/routes:
- SmsSettingsController gains create + test_message; the test_message
action lets an admin verify their config with a one-off SMS
against any phone number.
- SubmittersSendSmsController#create powers the per-submitter
"Send SMS" button (mirrors SubmittersSendEmailController).
- Routes: resources :sms with create + test_message; submitters
nested resources :send_sms.
Views:
- app/views/sms_settings/index.html.erb — real form replacing the
"not bundled" placeholder. Status banner reflects live config.
Test-send card renders only when SMS is enabled.
- app/views/submissions/_send_sms_button.html.erb — was a permanently
disabled stub; now button_to the new send_sms endpoint when SMS
is configured and the submitter has a phone number. Falls back to
a tooltip explaining what's missing otherwise.
- app/views/submissions/_send_sms.html.erb — was a placeholder render;
now shows a real "send SMS on save" toggle when SMS is configured.
- app/views/personalization_settings/_signature_request_sms_form.html.erb
+ show.html.erb — per-account SMS body override with variable
documentation.
Smoke-tested in a built image:
- /settings/sms renders 200, all form fields present.
- /settings/personalization renders the SMS body field.
- With saved (bogus) creds, Sms.send_message hits BulkVS over HTTPS
and surfaces the real 401 as Sms::ProviderError — proves the
transport is wired, not just the boot path.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The rebrand commit (2796ddf4) deleted the `maybe_redirect_com` helper
from ApplicationController — it was a DocuSeal-specific `.co → .com`
301 redirect with no role in WaboSign — but missed four
`prepend_before_action :maybe_redirect_com` / `before_action` callsites
that referenced it. Rails resolves before_action symbols at request
time via `send`, so every gated action raised NoMethodError → HTTP 500.
Affected URLs (now fixed):
- GET /settings/esign (admin)
- GET /submissions/:id (admin)
- GET /start_form/:slug + /completed (public — signers)
- GET /submissions/preview/:slug + /completed (public)
No replacement needed — the original redirect was DocuSeal-specific.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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>
Devise raises "Mapping omniauth_callbacks on a resource that is not
omniauthable" at route-load time if `controllers[:omniauth_callbacks]`
is set on `devise_for`, regardless of whether `:omniauth_callbacks` is
in the `only:` list (see devise-5.0.3 lib/devise/rails/routes.rb:251).
The previous routes.rb only gated the `only:` array. Adding the same
gate to the `controllers:` hash so the omniauth callback controller is
only declared when GOOGLE_CLIENT_ID + GOOGLE_CLIENT_SECRET are present.
Reproduced the crash by running the image without the env vars; fix
verified in both states:
- No env vars: container boots, /sign_in renders 200, no Google
button present.
- Env vars set + admin user created: container boots, /sign_in
renders 200, "Sign in with Google" form posts to /auth/google_oauth2
with the /google_g.svg logo.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The image now lives at GHCR (pushed manually as :latest and
:sha-45ed368a). Aligning the rest of the repo to match:
- docker-compose.yml: image: ghcr.io/wabolabs/wabosign:latest
- README.md docker-run example: same
- .github/workflows/docker.yml: meta.images and login-action repointed
at ghcr.io. Login now uses the auto-provisioned GITHUB_TOKEN with a
job-level `permissions: packages: write`; the DOCKERHUB_USERNAME /
DOCKERHUB_TOKEN secrets are no longer needed and can be removed from
the repo settings. Also adds a `type=raw,value=latest` tag so the
semver-triggered build keeps :latest pointing at the newest release.
- REBRANDING.md: registry note updated for accuracy.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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>
Adds the resolved entries for omniauth 2.1.4, omniauth-google-oauth2
1.2.2, omniauth-rails_csrf_protection 1.0.2, and their transitive
deps (omniauth-oauth2, oauth2, hashie, multi_xml). Should have shipped
with the SSO commit; pulled out here so the prior commit stays
reviewable and bundle install no longer regenerates the file at boot.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds "Sign in with Google" as an additive auth path next to email and
password. When GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET are set, the
Google button appears on the sign-in page and the SSO settings page
shows an env-driven status panel. Access is restricted to Workspace
domains listed in GOOGLE_ALLOWED_DOMAINS (CSV); the hd claim is
re-verified server-side on every callback so a misconfigured Google
consent screen cannot bypass it.
New users from an allowed domain are JIT-provisioned in the default
account (oldest, or pinned via GOOGLE_DEFAULT_ACCOUNT_ID). Existing
users with a matching email get linked to their Google identity on
first sign-in; identity collisions (same email, different Google uid)
are rejected.
Google's MFA is trusted: users signed in via Google do not see the
WaboSign OTP prompt or the FORCE_MFA setup redirect. Password sign-in
keeps working unchanged, including its existing OTP gate.
Implementation:
- Devise gains :omniauthable when SSO is enabled; users get
provider/uid columns with a partial unique index that allows NULL
for password-only rows.
- Users::OmniauthCallbacksController handles /users/auth/google_oauth2/
callback, sets session[:bypass_otp_for_sso], and redirects on failure.
- SessionsController#destroy clears the bypass flag on sign-out.
- DashboardController#maybe_redirect_mfa_setup honours the flag and
User#signed_in_via_sso?.
- The previously empty _omniauthable.html.erb stub now renders the
Google button.
Request specs cover happy path, link-existing-user, domain rejection,
identity collision, and 2FA bypass.
GOOGLE_SSO.md is the operator-facing setup, behaviour, verification,
and troubleshooting guide. README links to it.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Renames the product to WaboSign across UI, mailers, locales, assets, and
internal Ruby module. Keeps the upstream DocuSeal attribution required by
AGPLv3 §7(b) in the powered-by footer, email attribution, README, and a
new NOTICE file. Migration renames the AATL cert identifier in encrypted
configs from docuseal_aatl to wabosign_aatl.
Removes multitenant-gated Pro upsell UI (Plans/Console/Upgrade links,
SMS/SSO/bulk-send/logo placeholders, reminder-duration restriction, the
"DocuSeal Pro" email-attribution toggle, conditions/formula/payment
pricing links) so every shipped feature is reachable on a self-hosted
deployment. Multitenant routing logic is preserved.
Drops Discord, Twitter, and ChatGPT/AI-assistant chrome. Embedding
modal keeps the upstream <docuseal-form> / @docuseal/* SDK contract so
existing embedded forms continue to work; documented in NOTICE.
REBRANDING.md captures the change inventory for future maintainers.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>