From eb2d7379bf2669269b72c5aebeb3b3e828b06e9c Mon Sep 17 00:00:00 2001 From: Dan Milne Date: Thu, 27 Nov 2025 11:52:25 +1100 Subject: [PATCH] Backchannel complete - improve oidc credential display --- app/jobs/backchannel_logout_job.rb | 52 +++++++++++++++++++ ..._backchannel_logout_uri_to_applications.rb | 5 ++ 2 files changed, 57 insertions(+) create mode 100644 app/jobs/backchannel_logout_job.rb create mode 100644 db/migrate/20251125081147_add_backchannel_logout_uri_to_applications.rb diff --git a/app/jobs/backchannel_logout_job.rb b/app/jobs/backchannel_logout_job.rb new file mode 100644 index 0000000..ee37a87 --- /dev/null +++ b/app/jobs/backchannel_logout_job.rb @@ -0,0 +1,52 @@ +class BackchannelLogoutJob < ApplicationJob + queue_as :default + + # Retry with exponential backoff: 1s, 5s, 25s + retry_on StandardError, wait: :exponentially_longer, attempts: 3 + + def perform(user_id:, application_id:, consent_sid:) + # Find the records + user = User.find_by(id: user_id) + application = Application.find_by(id: application_id) + consent = OidcUserConsent.find_by(sid: consent_sid) + + # Validate we have all required data + unless user && application && consent + Rails.logger.warn "BackchannelLogout: Missing data - user: #{user.present?}, app: #{application.present?}, consent: #{consent.present?}" + return + end + + # Skip if application doesn't support backchannel logout + unless application.supports_backchannel_logout? + Rails.logger.debug "BackchannelLogout: Application #{application.name} doesn't support backchannel logout" + return + end + + # Generate the logout token + logout_token = OidcJwtService.generate_logout_token(user, application, consent) + + # Send HTTP POST to the application's backchannel logout URI + uri = URI.parse(application.backchannel_logout_uri) + + begin + response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https', open_timeout: 5, read_timeout: 5) do |http| + request = Net::HTTP::Post.new(uri.path.presence || '/') + request['Content-Type'] = 'application/x-www-form-urlencoded' + request.set_form_data({ logout_token: logout_token }) + http.request(request) + end + + if response.code.to_i == 200 + Rails.logger.info "BackchannelLogout: Successfully sent logout notification to #{application.name} (#{application.backchannel_logout_uri})" + else + Rails.logger.warn "BackchannelLogout: Application #{application.name} returned HTTP #{response.code} from #{application.backchannel_logout_uri}" + end + rescue Net::OpenTimeout, Net::ReadTimeout => e + Rails.logger.warn "BackchannelLogout: Timeout sending logout to #{application.name} (#{application.backchannel_logout_uri}): #{e.message}" + raise # Retry on timeout + rescue StandardError => e + Rails.logger.error "BackchannelLogout: Failed to send logout to #{application.name} (#{application.backchannel_logout_uri}): #{e.class} - #{e.message}" + raise # Retry on error + end + end +end diff --git a/db/migrate/20251125081147_add_backchannel_logout_uri_to_applications.rb b/db/migrate/20251125081147_add_backchannel_logout_uri_to_applications.rb new file mode 100644 index 0000000..0929e3a --- /dev/null +++ b/db/migrate/20251125081147_add_backchannel_logout_uri_to_applications.rb @@ -0,0 +1,5 @@ +class AddBackchannelLogoutUriToApplications < ActiveRecord::Migration[8.1] + def change + add_column :applications, :backchannel_logout_uri, :string + end +end