From ad2aa7b52ca5682e403e8d60231ff1bc3d7544ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matt=20Marjanovi=C4=87?= <53354851+mdoggydog@users.noreply.github.com> Date: Mon, 3 Feb 2020 09:53:14 -0800 Subject: [PATCH] FEATURE: Add logout functionality to SSO Provider protocol (#8816) This commit adds support for an optional "logout" parameter in the payload of the /session/sso_provider endpoint. If an SSO Consumer adds a "logout=true" parameter to the encoded/signed "sso" payload, then Discourse will treat the request as a logout request instead of an authentication request. The logout flow works something like this: * User requests logout at SSO-Consumer site (e.g., clicks "Log me out!" on web browser). * SSO-Consumer site does whatever it does to destroy User's session on the SSO-Consumer site. * SSO-Consumer then redirects browser to the Discourse sso_provider endpoint, with a signed request bearing "logout=true" in addition to the usual nonce and the "return_sso_url". * Discourse destroys User's discourse session and redirects browser back to the "return_sso_url". * SSO-Consumer site does whatever it does --- notably, it cannot request SSO credentials from Discourse without the User being prompted to login again. --- app/controllers/session_controller.rb | 6 ++++++ lib/single_sign_on.rb | 2 ++ spec/requests/session_controller_spec.rb | 26 ++++++++++++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/app/controllers/session_controller.rb b/app/controllers/session_controller.rb index 6103d012205..e9cfa31aa5b 100644 --- a/app/controllers/session_controller.rb +++ b/app/controllers/session_controller.rb @@ -54,6 +54,12 @@ class SessionController < ApplicationController return end + if sso.logout + params[:return_url] = sso.return_sso_url + destroy + return + end + if current_user sso.name = current_user.name sso.username = current_user.username diff --git a/lib/single_sign_on.rb b/lib/single_sign_on.rb index 4966efff511..a8e8ea79059 100644 --- a/lib/single_sign_on.rb +++ b/lib/single_sign_on.rb @@ -16,6 +16,7 @@ class SingleSignOn groups locale locale_force_update + logout name nonce profile_background_url @@ -34,6 +35,7 @@ class SingleSignOn admin avatar_force_update locale_force_update + logout moderator require_activation suppress_welcome_message diff --git a/spec/requests/session_controller_spec.rb b/spec/requests/session_controller_spec.rb index e7e54946f19..c70448b4e7a 100644 --- a/spec/requests/session_controller_spec.rb +++ b/spec/requests/session_controller_spec.rb @@ -1105,6 +1105,32 @@ RSpec.describe SessionController do expect(sso2.profile_background_url).to start_with(SiteSetting.s3_cdn_url) expect(sso2.card_background_url).to start_with(SiteSetting.s3_cdn_url) end + + it "successfully logs out and redirects user to return_sso_url when the user is logged in" do + sign_in(@user) + + @sso.logout = true + get "/session/sso_provider", params: Rack::Utils.parse_query(@sso.payload("secretForOverRainbow")) + + location = response.header["Location"] + expect(location).to match(/^http:\/\/somewhere.over.rainbow\/sso$/) + + expect(response.status).to eq(302) + expect(session[:current_user_id]).to be_blank + expect(response.cookies["_t"]).to be_blank + end + + it "successfully logs out and redirects user to return_sso_url when the user is not logged in" do + @sso.logout = true + get "/session/sso_provider", params: Rack::Utils.parse_query(@sso.payload("secretForOverRainbow")) + + location = response.header["Location"] + expect(location).to match(/^http:\/\/somewhere.over.rainbow\/sso$/) + + expect(response.status).to eq(302) + expect(session[:current_user_id]).to be_blank + expect(response.cookies["_t"]).to be_blank + end end end