mirror of
https://gh.wpcy.net/https://github.com/discourse/discourse.git
synced 2026-05-06 00:10:52 +08:00
Since the introduction of dedicated login and signup pages (as opposed to modals), we've been seeing reports of issues where visitors aren't redirected back to the "page" they were at when they initiated the _authentication_ process. Since we have a bazillion of ways a user might authenticate (credentials, social logins, SSO, passkeys, discourse connect, etc...), it's really hard to know what a change will impact. The goal of this PR is to "simplify" the way we handle this "redirection back to origin" by leveraging the use of a single `destination_url` cookie set on the client-side. The changes remove scattered cookie-setting code and consolidate the redirection logic to ensure users are properly redirected back to their original page after authentication. - Centralized destination URL cookie management in routes and authentication flows - Removed manual cookie setting from various components in favor of automatic handling - Updated test scenarios to properly test the new redirection behavior
158 lines
5.4 KiB
Ruby
158 lines
5.4 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
describe "User preferences | Security", type: :system do
|
|
fab!(:password) { "kungfukenny" }
|
|
fab!(:email) { "email@user.com" }
|
|
fab!(:user) { Fabricate(:user, email: email, password: password) }
|
|
let(:user_preferences_security_page) { PageObjects::Pages::UserPreferencesSecurity.new }
|
|
let(:user_menu) { PageObjects::Components::UserMenu.new }
|
|
|
|
before do
|
|
user.activate
|
|
# testing the enforced 2FA flow requires a user that was created > 5 minutes ago
|
|
user.created_at = 6.minutes.ago
|
|
user.save!
|
|
sign_in(user)
|
|
|
|
# system specs run on their own host + port
|
|
DiscourseWebauthn.stubs(:origin).returns(current_host + ":" + Capybara.server_port.to_s)
|
|
end
|
|
|
|
shared_examples "security keys" do
|
|
it "adds a 2FA security key and logs in with it" do
|
|
with_virtual_authenticator do
|
|
confirm_session_modal =
|
|
user_preferences_security_page
|
|
.visit(user)
|
|
.click_manage_2fa_authentication
|
|
.click_forgot_password
|
|
|
|
expect(confirm_session_modal).to have_forgot_password_email_sent
|
|
|
|
confirm_session_modal.submit_password(password)
|
|
|
|
expect(page).to have_current_path("/u/#{user.username}/preferences/second-factor")
|
|
|
|
find(".security-key .new-security-key").click
|
|
expect(user_preferences_security_page).to have_css("input#security-key-name")
|
|
|
|
find(".d-modal__body input#security-key-name").fill_in(with: "First Key")
|
|
find(".add-security-key").click
|
|
|
|
expect(user_preferences_security_page).to have_css(".security-key .second-factor-item")
|
|
|
|
user_menu.sign_out
|
|
|
|
# puts <<~STRING
|
|
# public_key_base64 = \"#{user.second_factor_security_keys.first.public_key}\"
|
|
# private_key_string = \"#{authenticator.credentials.first.private_key}\"
|
|
# STRING
|
|
|
|
# login flow
|
|
find(".d-header .login-button").click
|
|
find("input#login-account-name").fill_in(with: user.username)
|
|
find("input#login-account-password").fill_in(with: password)
|
|
|
|
find("#login-button.btn-primary").click
|
|
find("#security-key .btn-primary").click
|
|
|
|
expect(page).to have_css(".header-dropdown-toggle.current-user")
|
|
end
|
|
end
|
|
end
|
|
|
|
shared_examples "passkeys" do
|
|
before { SiteSetting.enable_passkeys = true }
|
|
|
|
it "adds a passkey, removes user password, logs in with passkey" do
|
|
with_virtual_authenticator(
|
|
hasUserVerification: true,
|
|
hasResidentKey: true,
|
|
isUserVerified: true,
|
|
) do
|
|
user_preferences_security_page.visit(user)
|
|
|
|
find(".pref-passkeys__add .btn").click
|
|
expect(user_preferences_security_page).to have_css("input#password")
|
|
|
|
find(".dialog-body input#password").fill_in(with: password)
|
|
find(".confirm-session .btn-primary").click
|
|
|
|
expect(user_preferences_security_page).to have_css(".rename-passkey__form")
|
|
|
|
find(".dialog-close").click
|
|
|
|
expect(user_preferences_security_page).to have_css(".pref-passkeys__rows .row")
|
|
|
|
select_kit = PageObjects::Components::SelectKit.new(".passkey-options-dropdown")
|
|
select_kit.expand
|
|
select_kit.select_row_by_name("Delete")
|
|
|
|
# confirm deletion screen shown without requiring session confirmation
|
|
# since this was already done when adding the passkey
|
|
expect(user_preferences_security_page).to have_css(".dialog-footer .btn-danger")
|
|
|
|
# close the dialog (don't delete the key, we need it to login in the next step)
|
|
find(".dialog-close").click
|
|
|
|
find("#remove-password-link").click
|
|
# already confirmed session for the passkey, so this will go straight for the confirmation dialog
|
|
find(".dialog-footer .btn-danger").click
|
|
expect(user_preferences_security_page).to have_no_css("#remove-password-link")
|
|
|
|
user_menu.sign_out
|
|
|
|
# ensures /hot isn't the homepage (otherwise the test below is pointless)
|
|
expect(SiteSetting.top_menu_items.first).not_to eq("hot")
|
|
|
|
# visit /hot to ensure we have a destination_url cookie set
|
|
visit("/hot")
|
|
|
|
# login with the key we just created
|
|
# this triggers the conditional UI for passkeys
|
|
# which uses the virtual authenticator
|
|
find(".d-header .login-button").click
|
|
|
|
expect(page).to have_css(".header-dropdown-toggle.current-user")
|
|
|
|
# ensures that we are redirected to the destination_url cookie
|
|
expect(page.driver.current_url).to include("/hot")
|
|
end
|
|
end
|
|
end
|
|
|
|
shared_examples "enforced second factor" do
|
|
it "allows user to add 2FA" do
|
|
SiteSetting.enforce_second_factor = "all"
|
|
|
|
visit("/")
|
|
|
|
expect(page).to have_selector(
|
|
".alert-error",
|
|
text: "You are required to enable two-factor authentication before accessing this site.",
|
|
)
|
|
|
|
expect(page).to have_css(".user-preferences .totp")
|
|
expect(page).to have_css(".user-preferences .security-key")
|
|
|
|
find(".user-preferences .totp .btn.new-totp").click
|
|
|
|
find(".dialog-body input#password").fill_in(with: password)
|
|
find(".confirm-session .btn-primary").click
|
|
|
|
expect(page).to have_css(".qr-code")
|
|
end
|
|
end
|
|
|
|
context "when desktop" do
|
|
include_examples "security keys"
|
|
include_examples "passkeys"
|
|
include_examples "enforced second factor"
|
|
end
|
|
|
|
context "when mobile", mobile: true do
|
|
include_examples "security keys"
|
|
include_examples "passkeys"
|
|
include_examples "enforced second factor"
|
|
end
|
|
end
|