From 65d7ea2dbc9e7bf276e5ac3f9e23c2111e64e278 Mon Sep 17 00:00:00 2001 From: Penar Musaraj Date: Thu, 13 Feb 2025 10:35:00 -0500 Subject: [PATCH] FIX: Keep user in same context after login (#31314) This fixes the destination of the auth process in the following scenarios: - when landing on a PM or a topic as an anonymous user and then loggin in - when landing on a public topic, hitting Reply or Like and then logging in --- .../discourse/app/components/modal/login.js | 4 ++ .../discourse/app/controllers/login.js | 4 ++ .../discourse/app/routes/application.js | 3 + .../javascripts/discourse/app/routes/login.js | 11 +++- spec/system/login_spec.rb | 57 ++++++++++++++++++- 5 files changed, 75 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/discourse/app/components/modal/login.js b/app/assets/javascripts/discourse/app/components/modal/login.js index d50dbd292a0..1d895b21fdc 100644 --- a/app/assets/javascripts/discourse/app/components/modal/login.js +++ b/app/assets/javascripts/discourse/app/components/modal/login.js @@ -137,6 +137,8 @@ export default class Login extends Component { } else if (destinationUrl) { removeCookie("destination_url"); window.location.assign(destinationUrl); + } else if (this.args.model.referrerUrl) { + window.location.assign(this.args.model.referrerUrl); } else { window.location.reload(); } @@ -288,6 +290,8 @@ export default class Login extends Component { removeCookie("destination_url"); applyHiddenFormInputValue(destinationUrl, "redirect"); + } else if (this.args.model.referrerUrl) { + applyHiddenFormInputValue(this.args.model.referrerUrl, "redirect"); } else { applyHiddenFormInputValue(window.location.href, "redirect"); } diff --git a/app/assets/javascripts/discourse/app/controllers/login.js b/app/assets/javascripts/discourse/app/controllers/login.js index 559ae4370b6..17fe482b46d 100644 --- a/app/assets/javascripts/discourse/app/controllers/login.js +++ b/app/assets/javascripts/discourse/app/controllers/login.js @@ -160,6 +160,8 @@ export default class LoginPageController extends Controller { } else if (destinationUrl) { removeCookie("destination_url"); window.location.assign(destinationUrl); + } else if (this.referrerUrl) { + window.location.assign(this.referrerUrl); } else { window.location.reload(); } @@ -337,6 +339,8 @@ export default class LoginPageController extends Controller { removeCookie("destination_url"); applyHiddenFormInputValue(destinationUrl, "redirect"); + } else if (this.referrerUrl) { + applyHiddenFormInputValue(this.referrerUrl, "redirect"); } else { applyHiddenFormInputValue(window.location.href, "redirect"); } diff --git a/app/assets/javascripts/discourse/app/routes/application.js b/app/assets/javascripts/discourse/app/routes/application.js index d680a6ac790..58076ee7338 100644 --- a/app/assets/javascripts/discourse/app/routes/application.js +++ b/app/assets/javascripts/discourse/app/routes/application.js @@ -298,6 +298,9 @@ export default class ApplicationRoute extends DiscourseRoute { showNotActivated: (props) => this.send("showNotActivated", props), showCreateAccount: (props) => this.send("showCreateAccount", props), canSignUp: this.controller.canSignUp, + referrerUrl: DiscourseURL.isInternal(document.referrer) + ? document.referrer + : null, }, }); } diff --git a/app/assets/javascripts/discourse/app/routes/login.js b/app/assets/javascripts/discourse/app/routes/login.js index 988a3e6024b..8c6c79a6b74 100644 --- a/app/assets/javascripts/discourse/app/routes/login.js +++ b/app/assets/javascripts/discourse/app/routes/login.js @@ -1,5 +1,6 @@ import { next } from "@ember/runloop"; import { service } from "@ember/service"; +import DiscourseURL from "discourse/lib/url"; import { defaultHomepage } from "discourse/lib/utilities"; import StaticPage from "discourse/models/static-page"; import DiscourseRoute from "discourse/routes/discourse"; @@ -9,7 +10,11 @@ export default class LoginRoute extends DiscourseRoute { @service router; @service login; - beforeModel() { + beforeModel(transition) { + if (transition.from) { + this.internalReferrer = this.router.urlFor(transition.from.name); + } + if (this.siteSettings.login_required) { if ( this.login.isOnlyOneExternalLoginMethod && @@ -49,6 +54,10 @@ export default class LoginRoute extends DiscourseRoute { controller.set("flashType", ""); controller.set("flash", ""); + if (this.internalReferrer || DiscourseURL.isInternal(document.referrer)) { + controller.set("referrerUrl", this.internalReferrer || document.referrer); + } + if (this.siteSettings.login_required) { controller.set("showLogin", false); } diff --git a/spec/system/login_spec.rb b/spec/system/login_spec.rb index 87ed75f0679..37ab1074c9f 100644 --- a/spec/system/login_spec.rb +++ b/spec/system/login_spec.rb @@ -88,9 +88,6 @@ shared_examples "login scenarios" do |login_page_object| # TODO: prefill username when fullpage if find("#username-or-email").value.blank? - if page.has_css?("html.mobile-view", wait: 0) - expect(page).to have_no_css(".d-modal.is-animating") - end find("#username-or-email").fill_in(with: user.username) end @@ -126,6 +123,60 @@ shared_examples "login scenarios" do |login_page_object| login_form.fill(username: "john", password: "supersecurepassword").click_login expect(page).to have_css(".header-dropdown-toggle.current-user") end + + it "redirects to a PM after login" do + EmailToken.confirm(Fabricate(:email_token, user: user).token) + + group = Fabricate(:group, publish_read_state: true) + Fabricate(:group_user, group: group, user: user) + pm = Fabricate(:private_message_topic, allowed_groups: [group]) + Fabricate(:post, topic: pm, user: user, reads: 2, created_at: 1.day.ago) + Fabricate(:group_private_message_topic, user: user, recipient_group: group) + + visit "/t/#{pm.id}" + find(".login-welcome .login-button").click + login_form.fill(username: "john", password: "supersecurepassword").click_login + + expect(page).to have_css(".header-dropdown-toggle.current-user") + expect(page).to have_css("#topic-title") + expect(page).to have_css(".private_message") + end + end + + context "when login is not required" do + before { SiteSetting.login_required = false } + + it "redirects to a PM after authentication" do + EmailToken.confirm(Fabricate(:email_token, user: user).token) + group = Fabricate(:group, publish_read_state: true) + Fabricate(:group_user, group: group, user: user) + pm = Fabricate(:private_message_topic, allowed_groups: [group]) + Fabricate(:post, topic: pm, user: user, reads: 2, created_at: 1.day.ago) + Fabricate(:group_private_message_topic, user: user, recipient_group: group) + + visit "/t/#{pm.id}" + find(".btn.login-button").click + + login_form.fill(username: "john", password: "supersecurepassword").click_login + expect(page).to have_css(".header-dropdown-toggle.current-user") + + expect(page).to have_css("#topic-title") + expect(page).to have_css(".private_message") + end + + it "redirects to a public topic when hitting Reply then logging in" do + EmailToken.confirm(Fabricate(:email_token, user: user).token) + topic = Fabricate(:topic) + Fabricate(:post, topic: topic, created_at: 1.day.ago) + + visit "/t/#{topic.id}" + find(".topic-footer-main-buttons .btn-primary").click + + login_form.fill(username: "john", password: "supersecurepassword").click_login + expect(page).to have_css(".header-dropdown-toggle.current-user") + + expect(page).to have_css("#topic-title") + end end context "with two-factor authentication" do