discourse/spec/system/discourse_connect_spec.rb
Régis Hanol 1fea84b493
FIX: redirect to 'private' topic when using discourse connect (#34176)
When an "anon" user visits a topic that is "private", they will get a
page that is server-rendered and doesn't load the client-side app.

When they click the "login" button, it will redirect to the /login route
which loads the app. The only way we can know where the user landed
initially is via the "document.referrer".

The issue was that the code that checked the referrer was **after** the
code that is doing the redirection to discourse connect.

By putting the code before doing the redirection to discourse connect,
we are successfully redirected to the landing url after doing the
discourse connect auth.

I've also added a system spec that test the happy path by starting the
most basic discourse connect compliant sso server in the background.
2025-08-08 18:52:10 +02:00

84 lines
2.1 KiB
Ruby
Vendored

# frozen_string_literal: true
require "webrick"
describe "Discourse Connect", type: :system do
let(:sso_secret) { SecureRandom.alphanumeric(32) }
let(:sso_port) { 9876 }
let(:sso_url) { "http://localhost:#{sso_port}/sso" }
fab!(:user)
fab!(:private_group) { Fabricate(:group, users: [user]) }
fab!(:private_category) { Fabricate(:private_category, group: private_group) }
fab!(:private_topic) { Fabricate(:topic, category: private_category) }
fab!(:private_post) { Fabricate(:post, topic: private_topic) }
before do
setup_test_sso_server
configure_discourse_connect
end
after { shutdown_test_sso_server }
context "when auth_immediately is enabled" do
before { SiteSetting.auth_immediately = true }
it "redirects the user back to the landing URL" do
visit private_topic.url
find(".login-button").click
wait_for { has_css?("#current-user") }
expect(page).to have_current_path(private_topic.relative_url)
end
end
private
def setup_test_sso_server
@server =
WEBrick::HTTPServer.new(
Port: sso_port,
Logger: WEBrick::Log.new(File.open(File::NULL, "w")),
AccessLog: [],
)
@server.mount_proc "/sso" do |req, res|
decoded = Base64.decode64(req.query["sso"])
params = Rack::Utils.parse_query(decoded)
response_sso = DiscourseConnectBase.new
response_sso.nonce = params["nonce"]
response_sso.sso_secret = sso_secret
response_sso.external_id = "foo-bar"
response_sso.email = user.email
response_sso.username = user.username
res.status = 302
res["Location"] = "#{params["return_sso_url"]}?#{response_sso.payload}"
end
@server_thread = Thread.new { @server.start }
sleep 0.1 until server_responding?
end
def shutdown_test_sso_server
@server&.shutdown
@server_thread&.kill
end
def server_responding?
Net::HTTP.get_response(URI(sso_url))
true
rescue StandardError
false
end
def configure_discourse_connect
SiteSetting.discourse_connect_url = sso_url
SiteSetting.discourse_connect_secret = sso_secret
SiteSetting.enable_discourse_connect = true
end
end