mirror of https://github.com/docusealco/docuseal
Reads the __session cookie set by accounts.bloombilt.com on the .bloombilt.com apex, verifies it via the official Clerk Ruby SDK, then finds or auto-provisions the matching Devise User on Account.first so the rest of the app (CanCanCan + Devise) sees the request as authenticated. Sign-out and unauthed redirects both target accounts.bloombilt.com/sign-in so 1Password sees a single saved entry across all Bloombilt apps. This is independent of the dead Clerk OIDC code already on master — that path requires Clerk Pro to register an OAuth Application on the production instance and is left dormant (gated by Docuseal.clerk_oidc_enabled?) in case we upgrade later. The session-cookie bridge works on Clerk free. Devise password login at /users/sign_in stays reachable as emergency access but isn't linked from the UI. Files: - Gemfile: add clerk-sdk-ruby (requires bundle install) - config/initializers/clerk.rb: SDK config (uses ENV['CLERK_SECRET_KEY']) - app/controllers/concerns/clerk_devise_bridge.rb: the bridge itself - app/controllers/application_controller.rb: include the concern, override authenticate_user! to redirect to Account Portal - app/controllers/sessions_controller.rb: override respond_to_on_destroy to send sign-out to Account Portal Gemfile.lock NOT updated in this commit — needs `bundle install` on a host with Ruby 4.0.1 before deploy will succeed.pull/688/head
parent
232380d378
commit
9e41d3a577
@ -0,0 +1,68 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Bridges Clerk's apex-cookie SSO into Devise. When a request arrives with a
|
||||
# valid Clerk session JWT belonging to the Bloombilt Staff org, find or
|
||||
# auto-provision the matching Devise User and call sign_in so the rest of the
|
||||
# app (which authorizes via Devise + CanCanCan) sees the request as
|
||||
# authenticated. Devise's password login at /users/sign_in is preserved as
|
||||
# emergency access.
|
||||
module ClerkDeviseBridge
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
STAFF_ORG_SLUG = 'bloombilt-staff'
|
||||
|
||||
included do
|
||||
before_action :sign_in_from_clerk_session, unless: :devise_controller?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def sign_in_from_clerk_session
|
||||
return if user_signed_in?
|
||||
|
||||
clerk_user = safe_clerk_user
|
||||
return unless clerk_user
|
||||
|
||||
return unless clerk.organization&.slug == STAFF_ORG_SLUG
|
||||
|
||||
email = primary_email(clerk_user)
|
||||
return unless email
|
||||
|
||||
user = User.active.find_by(email: email) || provision_user_for_clerk(email, clerk_user)
|
||||
return unless user
|
||||
|
||||
sign_in(user, store: true)
|
||||
end
|
||||
|
||||
def safe_clerk_user
|
||||
clerk.user
|
||||
rescue StandardError => e
|
||||
Rails.logger.warn("[clerk-bridge] reading clerk.user failed: #{e.class}: #{e.message}")
|
||||
nil
|
||||
end
|
||||
|
||||
def primary_email(clerk_user)
|
||||
addresses = clerk_user.respond_to?(:email_addresses) ? clerk_user.email_addresses : nil
|
||||
addresses&.first&.email_address&.to_s&.downcase.presence
|
||||
end
|
||||
|
||||
def provision_user_for_clerk(email, clerk_user)
|
||||
account = Account.first
|
||||
return nil unless account
|
||||
|
||||
first_name = clerk_user.respond_to?(:first_name) ? clerk_user.first_name : nil
|
||||
last_name = clerk_user.respond_to?(:last_name) ? clerk_user.last_name : nil
|
||||
|
||||
User.create!(
|
||||
account: account,
|
||||
email: email,
|
||||
first_name: first_name.presence,
|
||||
last_name: last_name.presence,
|
||||
role: User::ADMIN_ROLE,
|
||||
password: Devise.friendly_token(40)
|
||||
)
|
||||
rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotUnique => e
|
||||
Rails.logger.warn("[clerk-bridge] provision failed for #{email}: #{e.message}")
|
||||
User.active.find_by(email: email)
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Clerk SDK config — secret_key falls back to ENV['CLERK_SECRET_KEY'].
|
||||
# The Rack middleware is auto-mounted when the SDK is required, which
|
||||
# reads the __session cookie set by accounts.bloombilt.com on the apex
|
||||
# .bloombilt.com domain and exposes the verified user via the `clerk`
|
||||
# helper in controllers that include Clerk::Authenticatable.
|
||||
Clerk.configure do |c|
|
||||
c.logger = Rails.logger
|
||||
end
|
||||
Loading…
Reference in new issue