DEV: Move logout redirect logic to server and add plugin hook (#11199)
This will allow authentication plugins to provide single-logout functionality by redirect users to the identity provider after logout.
This commit is contained in:
parent
be07853cc1
commit
255633578c
|
@ -1,30 +1,16 @@
|
||||||
import getURL from "discourse-common/lib/get-url";
|
import getURL from "discourse-common/lib/get-url";
|
||||||
import { isEmpty } from "@ember/utils";
|
import { isEmpty } from "@ember/utils";
|
||||||
import { findAll } from "discourse/models/login-method";
|
|
||||||
import { helperContext } from "discourse-common/lib/helpers";
|
import { helperContext } from "discourse-common/lib/helpers";
|
||||||
|
|
||||||
export default function logout() {
|
export default function logout({ redirect } = {}) {
|
||||||
const ctx = helperContext();
|
const ctx = helperContext();
|
||||||
let siteSettings = ctx.siteSettings;
|
|
||||||
let keyValueStore = ctx.keyValueStore;
|
let keyValueStore = ctx.keyValueStore;
|
||||||
keyValueStore.abandonLocal();
|
keyValueStore.abandonLocal();
|
||||||
|
|
||||||
const redirect = siteSettings.logout_redirect;
|
|
||||||
if (!isEmpty(redirect)) {
|
if (!isEmpty(redirect)) {
|
||||||
window.location.href = redirect;
|
window.location.href = redirect;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sso = siteSettings.enable_sso;
|
|
||||||
const oneAuthenticator =
|
|
||||||
!siteSettings.enable_local_logins && findAll().length === 1;
|
|
||||||
|
|
||||||
if (siteSettings.login_required && (sso || oneAuthenticator)) {
|
|
||||||
// In this situation visiting most URLs will start the auth process again
|
|
||||||
// Go to the `/login` page to avoid an immediate redirect
|
|
||||||
window.location.href = getURL("/login");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.location.href = getURL("/");
|
window.location.href = getURL("/");
|
||||||
}
|
}
|
||||||
|
|
|
@ -278,7 +278,9 @@ const ApplicationRoute = DiscourseRoute.extend(OpenComposer, {
|
||||||
|
|
||||||
_handleLogout() {
|
_handleLogout() {
|
||||||
if (this.currentUser) {
|
if (this.currentUser) {
|
||||||
this.currentUser.destroySession().then(() => logout());
|
this.currentUser
|
||||||
|
.destroySession()
|
||||||
|
.then((response) => logout({ redirect: response["redirect_url"] }));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -442,12 +442,30 @@ class SessionController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
|
redirect_url = params[:return_url].presence || SiteSetting.logout_redirect.presence
|
||||||
|
|
||||||
|
sso = SiteSetting.enable_sso
|
||||||
|
only_one_authenticator = !SiteSetting.enable_local_logins && Discourse.enabled_authenticators.length == 1
|
||||||
|
if sso || only_one_authenticator
|
||||||
|
# In this situation visiting most URLs will start the auth process again
|
||||||
|
# Go to the `/login` page to avoid an immediate redirect
|
||||||
|
redirect_url ||= path("/login")
|
||||||
|
end
|
||||||
|
|
||||||
|
redirect_url ||= path("/")
|
||||||
|
|
||||||
|
event_data = { redirect_url: redirect_url, user: current_user }
|
||||||
|
DiscourseEvent.trigger(:before_session_destroy, event_data)
|
||||||
|
redirect_url = event_data[:redirect_url]
|
||||||
|
|
||||||
reset_session
|
reset_session
|
||||||
log_off_user
|
log_off_user
|
||||||
if request.xhr?
|
if request.xhr?
|
||||||
render body: nil
|
render json: {
|
||||||
|
redirect_url: redirect_url
|
||||||
|
}
|
||||||
else
|
else
|
||||||
redirect_to (params[:return_url] || path("/"))
|
redirect_to redirect_url
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1751,6 +1751,44 @@ RSpec.describe SessionController do
|
||||||
expect(session[:current_user_id]).to be_blank
|
expect(session[:current_user_id]).to be_blank
|
||||||
expect(response.cookies["_t"]).to be_blank
|
expect(response.cookies["_t"]).to be_blank
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'returns the redirect URL in the body for XHR requests' do
|
||||||
|
user = sign_in(Fabricate(:user))
|
||||||
|
delete "/session/#{user.username}.json", xhr: true
|
||||||
|
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
expect(session[:current_user_id]).to be_blank
|
||||||
|
expect(response.cookies["_t"]).to be_blank
|
||||||
|
|
||||||
|
expect(response.parsed_body["redirect_url"]).to eq("/")
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'redirects to /login for SSO' do
|
||||||
|
SiteSetting.sso_url = "https://example.com/sso"
|
||||||
|
SiteSetting.enable_sso = true
|
||||||
|
|
||||||
|
user = sign_in(Fabricate(:user))
|
||||||
|
delete "/session/#{user.username}.json", xhr: true
|
||||||
|
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
expect(response.parsed_body["redirect_url"]).to eq("/login")
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'allows plugins to manipulate redirect URL' do
|
||||||
|
callback = -> (data) do
|
||||||
|
data[:redirect_url] = "/myredirect/#{data[:user].username}"
|
||||||
|
end
|
||||||
|
|
||||||
|
DiscourseEvent.on(:before_session_destroy, &callback)
|
||||||
|
|
||||||
|
user = sign_in(Fabricate(:user))
|
||||||
|
delete "/session/#{user.username}.json", xhr: true
|
||||||
|
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
expect(response.parsed_body["redirect_url"]).to eq("/myredirect/#{user.username}")
|
||||||
|
ensure
|
||||||
|
DiscourseEvent.off(:before_session_destroy, &callback)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#one_time_password' do
|
describe '#one_time_password' do
|
||||||
|
|
Loading…
Reference in New Issue